From dc4de31f3edf9c9e3d56efad36a886e6c8329eb8 Mon Sep 17 00:00:00 2001 From: Danny Verkade Date: Wed, 16 May 2018 23:55:42 +0200 Subject: [PATCH 001/704] Fix #7333 Unable to set negative custom option fixed price in admin view. --- .../Option/Validator/DefaultValidator.php | 19 +++++++++++++++---- .../Model/Product/Option/Validator/Select.php | 2 +- .../Option/Validator/DefaultValidatorTest.php | 14 +++++++------- .../Product/Option/Validator/SelectTest.php | 3 +-- .../Product/Form/Modifier/CustomOptions.php | 2 +- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php index 1e5c7f76d829b..a1b80b1ac7c12 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php @@ -132,11 +132,11 @@ protected function validateOptionType(Option $option) */ protected function validateOptionValue(Option $option) { - return $this->isInRange($option->getPriceType(), $this->priceTypes) && !$this->isNegative($option->getPrice()); + return $this->isInRange($option->getPriceType(), $this->priceTypes) && $this->isNumber($option->getPrice()); } /** - * Check whether value is empty + * Check whether the value is empty * * @param mixed $value * @return bool @@ -147,7 +147,7 @@ protected function isEmpty($value) } /** - * Check whether value is in range + * Check whether the value is in range * * @param string $value * @param array $range @@ -159,7 +159,7 @@ protected function isInRange($value, array $range) } /** - * Check whether value is not negative + * Check whether the value is negative * * @param string $value * @return bool @@ -168,4 +168,15 @@ protected function isNegative($value) { return intval($value) < 0; } + + /** + * Check whether the value is a number + * + * @param string $value + * @return bool + */ + protected function isNumber($value) + { + return is_numeric($value); + } } diff --git a/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php b/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php index f04ab497e1d4f..af37c16c4053e 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php @@ -83,7 +83,7 @@ protected function isValidOptionPrice($priceType, $price, $storeId) if (!$priceType && !$price) { return true; } - if (!$this->isInRange($priceType, $this->priceTypes) || $this->isNegative($price)) { + if (!$this->isInRange($priceType, $this->priceTypes) || !$this->isNumber($price)) { return false; } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php index 1eb5f1a2dacd2..5e208c5d16604 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php @@ -129,11 +129,13 @@ public function testIsValidFail($product) * Data provider for testValidationNegativePrice * @return array */ - public function validationNegativePriceDataProvider() + public function validationPriceDataProvider() { return [ ['option_title', 'name 1.1', 'fixed', -12, new \Magento\Framework\DataObject(['store_id' => 1])], ['option_title', 'name 1.1', 'fixed', -12, new \Magento\Framework\DataObject(['store_id' => 0])], + ['option_title', 'name 1.1', 'fixed', 12, new \Magento\Framework\DataObject(['store_id' => 1])], + ['option_title', 'name 1.1', 'fixed', 12, new \Magento\Framework\DataObject(['store_id' => 0])] ]; } @@ -143,9 +145,9 @@ public function validationNegativePriceDataProvider() * @param $priceType * @param $price * @param $product - * @dataProvider validationNegativePriceDataProvider + * @dataProvider validationPriceDataProvider */ - public function testValidationNegativePrice($title, $type, $priceType, $price, $product) + public function testValidationPrice($title, $type, $priceType, $price, $product) { $methods = ['getTitle', 'getType', 'getPriceType', 'getPrice', '__wakeup', 'getProduct']; $valueMock = $this->createPartialMock(\Magento\Catalog\Model\Product\Option::class, $methods); @@ -155,10 +157,8 @@ public function testValidationNegativePrice($title, $type, $priceType, $price, $ $valueMock->expects($this->once())->method('getPrice')->will($this->returnValue($price)); $valueMock->expects($this->once())->method('getProduct')->will($this->returnValue($product)); - $messages = [ - 'option values' => 'Invalid option value', - ]; - $this->assertFalse($this->validator->isValid($valueMock)); + $messages = []; + $this->assertTrue($this->validator->isValid($valueMock)); $this->assertEquals($messages, $this->validator->getMessages()); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php index 046ee703c850e..924893bce23a4 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php @@ -87,7 +87,7 @@ public function isValidSuccessDataProvider() ] ], [ - false, + true, [ 'title' => 'Some Title', 'price_type' => 'fixed', @@ -157,7 +157,6 @@ public function testIsValidateWithInvalidData($priceType, $price, $title) public function isValidateWithInvalidDataDataProvider() { return [ - 'invalid_price' => ['fixed', -10, 'Title'], 'invalid_price_type' => ['some_value', '10', 'Title'], 'empty_title' => ['fixed', 10, null] ]; diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php index 7196a721f1d02..e557c8a377681 100755 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php @@ -923,7 +923,7 @@ protected function getPriceFieldConfig($sortOrder) 'addbeforePool' => $this->productOptionsPrice->prefixesToOptionArray(), 'sortOrder' => $sortOrder, 'validation' => [ - 'validate-zero-or-greater' => true + 'validate-number' => true ], ], ], From f7aab572bd188a002717a11f69d5bf572427dabd Mon Sep 17 00:00:00 2001 From: Danny Verkade Date: Sat, 2 Jun 2018 15:18:15 +0200 Subject: [PATCH 002/704] magento/magento2##7333: Unable to set negative custom option fixed price in admin view. - Fixed unit tests - Fixed integration tests - Changed defaultvalidator to have the locale format interface. Because a value entered in the backoffice of Magento is passed as the value like "40,000.00", which needs to be converted to a float in order to do a valid check on it. --- .../Option/Validator/DefaultValidator.php | 16 ++++++-- .../Option/Validator/DefaultValidatorTest.php | 15 +++++++- .../Product/Option/Validator/FileTest.php | 38 ++++++++++++++++++- .../Product/Option/Validator/SelectTest.php | 17 ++++++++- .../Product/Option/Validator/TextTest.php | 22 ++++++++++- 5 files changed, 101 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php index a1b80b1ac7c12..a01bc6b3e413f 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php @@ -25,13 +25,20 @@ class DefaultValidator extends \Magento\Framework\Validator\AbstractValidator */ protected $priceTypes; + /** + * @var \Magento\Framework\Locale\FormatInterface + */ + private $localeFormat; + /** * @param \Magento\Catalog\Model\ProductOptions\ConfigInterface $productOptionConfig * @param \Magento\Catalog\Model\Config\Source\Product\Options\Price $priceConfig + * @param \Magento\Framework\Locale\FormatInterface|null $localeFormat */ public function __construct( \Magento\Catalog\Model\ProductOptions\ConfigInterface $productOptionConfig, - \Magento\Catalog\Model\Config\Source\Product\Options\Price $priceConfig + \Magento\Catalog\Model\Config\Source\Product\Options\Price $priceConfig, + \Magento\Framework\Locale\FormatInterface $localeFormat = null ) { foreach ($productOptionConfig->getAll() as $option) { foreach ($option['types'] as $type) { @@ -42,6 +49,9 @@ public function __construct( foreach ($priceConfig->toOptionArray() as $item) { $this->priceTypes[] = $item['value']; } + + $this->localeFormat = $localeFormat ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Locale\FormatInterface::class); } /** @@ -166,7 +176,7 @@ protected function isInRange($value, array $range) */ protected function isNegative($value) { - return intval($value) < 0; + return $this->localeFormat->getNumber($value) < 0; } /** @@ -177,6 +187,6 @@ protected function isNegative($value) */ protected function isNumber($value) { - return is_numeric($value); + return is_numeric($this->localeFormat->getNumber($value)); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php index 5e208c5d16604..40c37731c0474 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php @@ -18,11 +18,18 @@ class DefaultValidatorTest extends \PHPUnit\Framework\TestCase */ protected $valueMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $localeFormatMock; + protected function setUp() { $configMock = $this->createMock(\Magento\Catalog\Model\ProductOptions\ConfigInterface::class); $storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); $priceConfigMock = new \Magento\Catalog\Model\Config\Source\Product\Options\Price($storeManagerMock); + $this->localeFormatMock = $this->createMock(\Magento\Framework\Locale\FormatInterface::class); + $config = [ [ 'label' => 'group label 1', @@ -48,7 +55,8 @@ protected function setUp() $configMock->expects($this->once())->method('getAll')->will($this->returnValue($config)); $this->validator = new \Magento\Catalog\Model\Product\Option\Validator\DefaultValidator( $configMock, - $priceConfigMock + $priceConfigMock, + $this->localeFormatMock ); } @@ -86,6 +94,9 @@ public function testIsValidTitle($title, $type, $priceType, $price, $product, $m $valueMock->expects($this->once())->method('getPriceType')->will($this->returnValue($priceType)); $valueMock->expects($this->once())->method('getPrice')->will($this->returnValue($price)); $valueMock->expects($this->once())->method('getProduct')->will($this->returnValue($product)); + + $this->localeFormatMock->expects($this->once())->method('getNumber')->will($this->returnValue($price)); + $this->assertEquals($result, $this->validator->isValid($valueMock)); $this->assertEquals($messages, $this->validator->getMessages()); } @@ -157,6 +168,8 @@ public function testValidationPrice($title, $type, $priceType, $price, $product) $valueMock->expects($this->once())->method('getPrice')->will($this->returnValue($price)); $valueMock->expects($this->once())->method('getProduct')->will($this->returnValue($product)); + $this->localeFormatMock->expects($this->once())->method('getNumber')->will($this->returnValue($price)); + $messages = []; $this->assertTrue($this->validator->isValid($valueMock)); $this->assertEquals($messages, $this->validator->getMessages()); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php index 3c06db0e7ce5f..f87bcb5d9bb06 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/FileTest.php @@ -18,11 +18,18 @@ class FileTest extends \PHPUnit\Framework\TestCase */ protected $valueMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $localeFormatMock; + protected function setUp() { $configMock = $this->createMock(\Magento\Catalog\Model\ProductOptions\ConfigInterface::class); $storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); $priceConfigMock = new \Magento\Catalog\Model\Config\Source\Product\Options\Price($storeManagerMock); + $this->localeFormatMock = $this->createMock(\Magento\Framework\Locale\FormatInterface::class); + $config = [ [ 'label' => 'group label 1', @@ -50,7 +57,8 @@ protected function setUp() $this->valueMock = $this->createPartialMock(\Magento\Catalog\Model\Product\Option::class, $methods); $this->validator = new \Magento\Catalog\Model\Product\Option\Validator\File( $configMock, - $priceConfigMock + $priceConfigMock, + $this->localeFormatMock ); } @@ -62,6 +70,15 @@ public function testIsValidSuccess() $this->valueMock->expects($this->once())->method('getPrice')->will($this->returnValue(10)); $this->valueMock->expects($this->once())->method('getImageSizeX')->will($this->returnValue(10)); $this->valueMock->expects($this->once())->method('getImageSizeY')->will($this->returnValue(15)); + $this->localeFormatMock->expects($this->at(0)) + ->method('getNumber') + ->with($this->equalTo(10)) + ->will($this->returnValue(10)); + $this->localeFormatMock + ->expects($this->at(2)) + ->method('getNumber') + ->with($this->equalTo(15)) + ->will($this->returnValue(15)); $this->assertEmpty($this->validator->getMessages()); $this->assertTrue($this->validator->isValid($this->valueMock)); } @@ -74,6 +91,16 @@ public function testIsValidWithNegativeImageSize() $this->valueMock->expects($this->once())->method('getPrice')->will($this->returnValue(10)); $this->valueMock->expects($this->once())->method('getImageSizeX')->will($this->returnValue(-10)); $this->valueMock->expects($this->never())->method('getImageSizeY'); + $this->localeFormatMock->expects($this->at(0)) + ->method('getNumber') + ->with($this->equalTo(10)) + ->will($this->returnValue(10)); + $this->localeFormatMock + ->expects($this->at(1)) + ->method('getNumber') + ->with($this->equalTo(-10)) + ->will($this->returnValue(-10)); + $messages = [ 'option values' => 'Invalid option value', ]; @@ -89,6 +116,15 @@ public function testIsValidWithNegativeImageSizeY() $this->valueMock->expects($this->once())->method('getPrice')->will($this->returnValue(10)); $this->valueMock->expects($this->once())->method('getImageSizeX')->will($this->returnValue(10)); $this->valueMock->expects($this->once())->method('getImageSizeY')->will($this->returnValue(-10)); + $this->localeFormatMock->expects($this->at(0)) + ->method('getNumber') + ->with($this->equalTo(10)) + ->will($this->returnValue(10)); + $this->localeFormatMock + ->expects($this->at(2)) + ->method('getNumber') + ->with($this->equalTo(-10)) + ->will($this->returnValue(-10)); $messages = [ 'option values' => 'Invalid option value', ]; diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php index 924893bce23a4..032eb624ed502 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/SelectTest.php @@ -18,11 +18,17 @@ class SelectTest extends \PHPUnit\Framework\TestCase */ protected $valueMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $localeFormatMock; + protected function setUp() { $configMock = $this->createMock(\Magento\Catalog\Model\ProductOptions\ConfigInterface::class); $storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); $priceConfigMock = new \Magento\Catalog\Model\Config\Source\Product\Options\Price($storeManagerMock); + $this->localeFormatMock = $this->createMock(\Magento\Framework\Locale\FormatInterface::class); $config = [ [ 'label' => 'group label 1', @@ -50,7 +56,8 @@ protected function setUp() $this->valueMock = $this->createPartialMock(\Magento\Catalog\Model\Product\Option::class, $methods, []); $this->validator = new \Magento\Catalog\Model\Product\Option\Validator\Select( $configMock, - $priceConfigMock + $priceConfigMock, + $this->localeFormatMock ); } @@ -66,6 +73,12 @@ public function testIsValidSuccess($expectedResult, array $value) $this->valueMock->expects($this->never())->method('getPriceType'); $this->valueMock->expects($this->never())->method('getPrice'); $this->valueMock->expects($this->any())->method('getData')->with('values')->will($this->returnValue([$value])); + if (isset($value['price'])) { + $this->localeFormatMock + ->expects($this->once()) + ->method('getNumber') + ->will($this->returnValue($value['price'])); + } $this->assertEquals($expectedResult, $this->validator->isValid($this->valueMock)); } @@ -108,6 +121,7 @@ public function testIsValidateWithInvalidOptionValues() ->method('getData') ->with('values') ->will($this->returnValue('invalid_data')); + $messages = [ 'option values' => 'Invalid option value', ]; @@ -147,6 +161,7 @@ public function testIsValidateWithInvalidData($priceType, $price, $title) $this->valueMock->expects($this->never())->method('getPriceType'); $this->valueMock->expects($this->never())->method('getPrice'); $this->valueMock->expects($this->any())->method('getData')->with('values')->will($this->returnValue([$value])); + $this->localeFormatMock->expects($this->any())->method('getNumber')->will($this->returnValue($price)); $messages = [ 'option values' => 'Invalid option value', ]; diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php index cf31d67817684..f3951081b37af 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/TextTest.php @@ -18,11 +18,17 @@ class TextTest extends \PHPUnit\Framework\TestCase */ protected $valueMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $localeFormatMock; + protected function setUp() { $configMock = $this->createMock(\Magento\Catalog\Model\ProductOptions\ConfigInterface::class); $storeManagerMock = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); $priceConfigMock = new \Magento\Catalog\Model\Config\Source\Product\Options\Price($storeManagerMock); + $this->localeFormatMock = $this->createMock(\Magento\Framework\Locale\FormatInterface::class); $config = [ [ 'label' => 'group label 1', @@ -50,7 +56,8 @@ protected function setUp() $this->valueMock = $this->createPartialMock(\Magento\Catalog\Model\Product\Option::class, $methods); $this->validator = new \Magento\Catalog\Model\Product\Option\Validator\Text( $configMock, - $priceConfigMock + $priceConfigMock, + $this->localeFormatMock ); } @@ -61,6 +68,10 @@ public function testIsValidSuccess() $this->valueMock->expects($this->once())->method('getPriceType')->will($this->returnValue('fixed')); $this->valueMock->expects($this->once())->method('getPrice')->will($this->returnValue(10)); $this->valueMock->expects($this->once())->method('getMaxCharacters')->will($this->returnValue(10)); + $this->localeFormatMock->expects($this->exactly(2)) + ->method('getNumber') + ->with($this->equalTo(10)) + ->will($this->returnValue(10)); $this->assertTrue($this->validator->isValid($this->valueMock)); $this->assertEmpty($this->validator->getMessages()); } @@ -72,6 +83,15 @@ public function testIsValidWithNegativeMaxCharacters() $this->valueMock->expects($this->once())->method('getPriceType')->will($this->returnValue('fixed')); $this->valueMock->expects($this->once())->method('getPrice')->will($this->returnValue(10)); $this->valueMock->expects($this->once())->method('getMaxCharacters')->will($this->returnValue(-10)); + $this->localeFormatMock->expects($this->at(0)) + ->method('getNumber') + ->with($this->equalTo(10)) + ->will($this->returnValue(10)); + $this->localeFormatMock + ->expects($this->at(1)) + ->method('getNumber') + ->with($this->equalTo(-10)) + ->will($this->returnValue(-10)); $messages = [ 'option values' => 'Invalid option value', ]; From 9a8e7afa278913afffda7dacf5de3355c58965ae Mon Sep 17 00:00:00 2001 From: Danny Verkade Date: Tue, 5 Jun 2018 11:42:21 +0200 Subject: [PATCH 003/704] magento/magento2#7333: Unable to set negative custom option fixed price in admin view. - Changed validator method to public --- .../Catalog/Model/Product/Option/Validator/DefaultValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php index a01bc6b3e413f..838d92fb5d038 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Validator/DefaultValidator.php @@ -185,7 +185,7 @@ protected function isNegative($value) * @param string $value * @return bool */ - protected function isNumber($value) + public function isNumber($value) { return is_numeric($this->localeFormat->getNumber($value)); } From 04caefa9b6c9bfe4fea6dc032b8e3cb61000f970 Mon Sep 17 00:00:00 2001 From: Danny Verkade Date: Tue, 10 Jul 2018 09:12:55 -0400 Subject: [PATCH 004/704] Fixed API test. --- .../Magento/Catalog/Api/_files/product_options.php | 12 +++++++++++- .../Catalog/Api/_files/product_options_negative.php | 11 ----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php index 144f3a9926fe3..955322929762d 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php @@ -10,7 +10,7 @@ 'type' => 'field', 'sort_order' => 1, 'is_require' => 1, - 'price' => 10, + 'price' => -10, 'price_type' => 'fixed', 'sku' => 'sku1', 'max_characters' => 10, @@ -153,4 +153,14 @@ 'price_type' => 'fixed', 'sku' => 'time option sku', ], + [ + 'title' => 'test negative price', + 'type' => 'field', + 'sort_order' => 1, + 'is_require' => 1, + 'price' => -10, + 'price_type' => 'fixed', + 'sku' => 'sku1', + 'max_characters' => 10, + ], ]; diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_negative.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_negative.php index 5d2737b3aa532..50b68a2653ed3 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_negative.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options_negative.php @@ -15,17 +15,6 @@ 'sku' => 'sku1', 'max_characters' => 10, ], - 'negative_price' => [ - 'title' => 'area option', - 'type' => 'area', - 'sort_order' => 2, - 'is_require' => 0, - 'price' => -20, - 'price_type' => 'percent', - 'sku' => 'sku2', - 'max_characters' => 20, - - ], 'negative_value_of_image_size' => [ 'title' => 'file option', 'type' => 'file', From 90b1c761e87f958cde0fda4710086234547d354e Mon Sep 17 00:00:00 2001 From: Saravanan Date: Mon, 27 Aug 2018 10:40:01 +0530 Subject: [PATCH 005/704] 17754-Fixed validation while creating new product attributes in admin --- .../Catalog/Controller/Adminhtml/Product/Attribute/Save.php | 4 ++-- lib/web/mage/validation.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php index 817de6828e48d..0b3b8b06c969f 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php @@ -167,12 +167,12 @@ public function execute() $attributeCode = $attributeCode ?: $this->generateCode($this->getRequest()->getParam('frontend_label')[0]); if (strlen($attributeCode) > 0) { $validatorAttrCode = new \Zend_Validate_Regex( - ['pattern' => '/^[a-z\x{600}-\x{6FF}][a-z\x{600}-\x{6FF}_0-9]{0,30}$/u'] + ['pattern' => '/^[a-zA-Z\x{600}-\x{6FF}][a-zA-Z\x{600}-\x{6FF}_0-9]{0,30}$/u'] ); if (!$validatorAttrCode->isValid($attributeCode)) { $this->messageManager->addErrorMessage( __( - 'Attribute code "%1" is invalid. Please use only letters (a-z), ' . + 'Attribute code "%1" is invalid. Please use only letters (a-z or A-Z), ' . 'numbers (0-9) or underscore(_) in this field, first character should be a letter.', $attributeCode ) diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index d08819ebe94aa..d0e77b802f77a 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -980,9 +980,9 @@ ], 'validate-code': [ function (v) { - return $.mage.isEmptyNoTrim(v) || /^[a-z]+[a-z0-9_]+$/.test(v); + return $.mage.isEmptyNoTrim(v) || /^[a-zA-Z]+[a-zA-Z0-9_]+$/.test(v); }, - $.mage.__('Please use only letters (a-z), numbers (0-9) or underscore (_) in this field, and the first character should be a letter.') //eslint-disable-line max-len + $.mage.__('Please use only letters (a-z or A-Z), numbers (0-9) or underscore (_) in this field, and the first character should be a letter.') //eslint-disable-line max-len ], 'validate-alphanum': [ function (v) { From 22c185efdf7c72f5967f0537b00af17df9d753d6 Mon Sep 17 00:00:00 2001 From: Saravanan Date: Mon, 27 Aug 2018 13:57:20 +0530 Subject: [PATCH 006/704] Updated the unit test controller to save the attribute --- .../Catalog/Controller/Adminhtml/Product/AttributeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php index 4261873cc8e6e..3d0c0a96988f5 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php @@ -147,7 +147,7 @@ public function testWrongAttributeCode() /** @var \Magento\Framework\Message\Error $message */ $message = $messages->getItemsByType('error')[0]; $this->assertEquals( - 'Attribute code "_()&&&?" is invalid. Please use only letters (a-z),' + 'Attribute code "_()&&&?" is invalid. Please use only letters (a-z or A-Z),' . ' numbers (0-9) or underscore(_) in this field, first character should be a letter.', $message->getText() ); From d43a52f4d919d0537a61028869f35a1f225aa324 Mon Sep 17 00:00:00 2001 From: Danny Verkade Date: Wed, 5 Sep 2018 10:33:27 +0200 Subject: [PATCH 007/704] Resolved merge conflict with merging 2.3 develop branch. --- .../Model/Product/Option/Validator/DefaultValidatorTest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php index 2ac000499a220..82019db2e128f 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php @@ -137,7 +137,6 @@ public function testIsValidFail($product) $this->assertFalse($this->validator->isValid($valueMock)); $this->assertEquals($messages, $this->validator->getMessages()); } -<<<<<<< HEAD /** * Data provider for testValidationNegativePrice @@ -177,6 +176,4 @@ public function testValidationPrice($title, $type, $priceType, $price, $product) $this->assertTrue($this->validator->isValid($valueMock)); $this->assertEquals($messages, $this->validator->getMessages()); } -======= ->>>>>>> 2.3-develop } From d66329a3f49c96525b998227505be47163a7e4de Mon Sep 17 00:00:00 2001 From: Danny Verkade Date: Wed, 5 Sep 2018 10:53:10 +0200 Subject: [PATCH 008/704] Fixed DefaultValidatorTest --- .../Option/Validator/DefaultValidatorTest.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php index 82019db2e128f..7c2ec8abb768a 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Validator/DefaultValidatorTest.php @@ -71,10 +71,10 @@ public function isValidTitleDataProvider() { $mess = ['option required fields' => 'Missed values for option required fields']; return [ - ['option_title', 'name 1.1', 'fixed', new \Magento\Framework\DataObject(['store_id' => 1]), [], true], - ['option_title', 'name 1.1', 'fixed', new \Magento\Framework\DataObject(['store_id' => 0]), [], true], - [null, 'name 1.1', 'fixed', new \Magento\Framework\DataObject(['store_id' => 1]), [], true], - [null, 'name 1.1', 'fixed', new \Magento\Framework\DataObject(['store_id' => 0]), $mess, false], + ['option_title', 'name 1.1', 'fixed', 10, new \Magento\Framework\DataObject(['store_id' => 1]), [], true], + ['option_title', 'name 1.1', 'fixed', 10, new \Magento\Framework\DataObject(['store_id' => 0]), [], true], + [null, 'name 1.1', 'fixed', 10, new \Magento\Framework\DataObject(['store_id' => 1]), [], true], + [null, 'name 1.1', 'fixed', 10, new \Magento\Framework\DataObject(['store_id' => 0]), $mess, false], ]; } @@ -87,14 +87,14 @@ public function isValidTitleDataProvider() * @param bool $result * @dataProvider isValidTitleDataProvider */ - public function testIsValidTitle($title, $type, $priceType, $product, $messages, $result) + public function testIsValidTitle($title, $type, $priceType, $price, $product, $messages, $result) { - $methods = ['getTitle', 'getType', 'getPriceType', '__wakeup', 'getProduct']; + $methods = ['getTitle', 'getType', 'getPriceType', 'getPrice', '__wakeup', 'getProduct']; $valueMock = $this->createPartialMock(\Magento\Catalog\Model\Product\Option::class, $methods); $valueMock->expects($this->once())->method('getTitle')->will($this->returnValue($title)); $valueMock->expects($this->any())->method('getType')->will($this->returnValue($type)); $valueMock->expects($this->once())->method('getPriceType')->will($this->returnValue($priceType)); - // $valueMock->expects($this->once())->method('getPrice')->will($this->returnValue($price)); + $valueMock->expects($this->once())->method('getPrice')->will($this->returnValue($price)); $valueMock->expects($this->once())->method('getProduct')->will($this->returnValue($product)); $this->localeFormatMock->expects($this->once())->method('getNumber')->will($this->returnValue($price)); From a21fed6f2e70e9f6191d94a7b37050c7221433a4 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Fri, 14 Sep 2018 16:17:32 +0300 Subject: [PATCH 009/704] MAGETWO-95034: Investigate ability of addresses grid displaying on frontent --- .../Magento/Customer/Block/Address/Book.php | 12 +++++ .../frontend/templates/address/book.phtml | 54 ++++++++++++++----- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Customer/Block/Address/Book.php b/app/code/Magento/Customer/Block/Address/Book.php index 8b38946a063db..e1c53356c7e69 100644 --- a/app/code/Magento/Customer/Block/Address/Book.php +++ b/app/code/Magento/Customer/Block/Address/Book.php @@ -212,4 +212,16 @@ public function getDefaultShipping() return $customer->getDefaultShipping(); } } + + /** + * @param $street + * @return string + */ + public function getStreetAddress($street) + { + if (is_array($street)) { + $street = implode(', ', $street); + } + return $street; + } } diff --git a/app/code/Magento/Customer/view/frontend/templates/address/book.phtml b/app/code/Magento/Customer/view/frontend/templates/address/book.phtml index 45c13d9357425..96b2f4dd0a1e1 100644 --- a/app/code/Magento/Customer/view/frontend/templates/address/book.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/address/book.phtml @@ -67,19 +67,45 @@
escapeHtml(__('Additional Address Entries')) ?>
getAdditionalAddresses()): ?> -
    - -
  1. -
    - getAddressHtml($_address) ?>
    -
    - -
  2. - -
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
getId() ?>getFirstname() ?>getLastname() ?>getStreetAddress($address->getStreet()) ?>getCity() ?>getCountryId() ?>getRegion()->getRegion() ?>getPostcode() ?>getTelephone() ?> + escapeHtml(__('Edit Address')) ?> + escapeHtml(__('Delete Address')) ?> +
+

escapeHtml(__('You have no other address entries in your address book.')) ?>

@@ -98,7 +124,7 @@ { ".page-main": { "address": { - "deleteAddress": "li.item a[role='delete-address']", + "deleteAddress": "td a[role='delete-address']", "deleteUrlPrefix": "escapeJs($block->escapeUrl($block->getDeleteUrl())) ?>id/", "addAddress": "button[role='add-address']", "addAddressLocation": "escapeJs($block->escapeUrl($block->getAddAddressUrl())) ?>" From 1e4dd7d86305c48709001082c4865b3460161cbd Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi Date: Wed, 19 Sep 2018 00:39:10 +0300 Subject: [PATCH 010/704] Clean code --- .../Catalog/Ui/Component/ColumnFactory.php | 20 +- .../Component/Listing/Columns/Thumbnail.php | 2 +- .../Model/ObjectRegistry.php | 6 +- .../Block/Adminhtml/Edit/Tab/View/Sales.php | 18 +- .../Model/Address/AbstractAddress.php | 2 +- app/code/Magento/Fedex/Model/Carrier.php | 13 +- .../IntegrationFactory.php | 2 +- .../DataProvider/NotificationDataProvider.php | 2 +- .../Magento/Sales/Model/Order/Address.php | 2 +- .../Sales/Model/Order/Payment/Info.php | 2 +- .../Sales/Model/Order/Payment/Transaction.php | 4 +- .../Model/ResourceModel/AbstractGrid.php | 2 +- .../Model/Carrier/AbstractCarrierOnline.php | 6 +- .../Request/AddressBuilder.php | 2 +- app/code/Magento/Store/Model/Store.php | 189 ++++++++++-------- .../Model/ResourceModel/Layout/Update.php | 34 ++-- 16 files changed, 168 insertions(+), 138 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php index cbc67fee8a5a3..8daec1fa6ca78 100644 --- a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php +++ b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php @@ -46,12 +46,14 @@ public function __construct(\Magento\Framework\View\Element\UiComponentFactory $ $this->componentFactory = $componentFactory; } - /** - * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute - * @param \Magento\Framework\View\Element\UiComponent\ContextInterface $context - * @param array $config - * @return \Magento\Ui\Component\Listing\Columns\ColumnInterface - */ + /** + * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute + * @param \Magento\Framework\View\Element\UiComponent\ContextInterface $context + * @param array $config + * + * @return \Magento\Ui\Component\Listing\Columns\ColumnInterface + * @throws \Magento\Framework\Exception\LocalizedException + */ public function create($attribute, $context, array $config = []) { $columnName = $attribute->getAttributeCode(); @@ -96,9 +98,7 @@ protected function getJsComponent($dataType) */ protected function getDataType($attribute) { - return isset($this->dataTypeMap[$attribute->getFrontendInput()]) - ? $this->dataTypeMap[$attribute->getFrontendInput()] - : $this->dataTypeMap['default']; + return $this->dataTypeMap[$attribute->getFrontendInput()] ?? $this->dataTypeMap['default']; } /** @@ -111,6 +111,6 @@ protected function getFilterType($frontendInput) { $filtersMap = ['date' => 'dateRange']; $result = array_replace_recursive($this->dataTypeMap, $filtersMap); - return isset($result[$frontendInput]) ? $result[$frontendInput] : $result['default']; + return $result[$frontendInput] ?? $result['default']; } } diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php index d4dc9ddd7ca3b..dc3c94f384632 100644 --- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php @@ -74,6 +74,6 @@ public function prepareDataSource(array $dataSource) protected function getAlt($row) { $altField = $this->getData('config/altField') ?: self::ALT_FIELD; - return isset($row[$altField]) ? $row[$altField] : null; + return $row[$altField] ?? null; } } diff --git a/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php b/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php index 019959f9c1fea..ecc4ff04ab8a7 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php @@ -26,15 +26,19 @@ public function __construct($entities) } /** + * Get + * * @param int $entityId * @return \Magento\Framework\DataObject|null */ public function get($entityId) { - return isset($this->entitiesMap[$entityId]) ? $this->entitiesMap[$entityId] : null; + return $this->entitiesMap[$entityId] ?? null; } /** + * List + * * @return \Magento\Framework\DataObject[] */ public function getList() diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Sales.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Sales.php index 76c33f143e671..b9442797765c6 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Sales.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Sales.php @@ -147,7 +147,7 @@ public function _beforeToHtml() */ public function getWebsiteCount($websiteId) { - return isset($this->_websiteCounts[$websiteId]) ? $this->_websiteCounts[$websiteId] : 0; + return $this->_websiteCounts[$websiteId] ?? 0; } /** @@ -166,13 +166,15 @@ public function getTotals() return $this->_collection->getTotals(); } - /** - * Format price by specified website - * - * @param float $price - * @param null|int $websiteId - * @return string - */ + /** + * Format price by specified website + * + * @param float $price + * @param null|int $websiteId + * + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ public function formatCurrency($price, $websiteId = null) { return $this->_storeManager->getWebsite($websiteId)->getBaseCurrency()->format($price); diff --git a/app/code/Magento/Customer/Model/Address/AbstractAddress.php b/app/code/Magento/Customer/Model/Address/AbstractAddress.php index 6408276630c3f..2c0c7812ca669 100644 --- a/app/code/Magento/Customer/Model/Address/AbstractAddress.php +++ b/app/code/Magento/Customer/Model/Address/AbstractAddress.php @@ -230,7 +230,7 @@ public function getStreet() public function getStreetLine($number) { $lines = $this->getStreet(); - return isset($lines[$number - 1]) ? $lines[$number - 1] : ''; + return $lines[$number - 1] ?? ''; } /** diff --git a/app/code/Magento/Fedex/Model/Carrier.php b/app/code/Magento/Fedex/Model/Carrier.php index f6e63e04ac559..843867e183cac 100644 --- a/app/code/Magento/Fedex/Model/Carrier.php +++ b/app/code/Magento/Fedex/Model/Carrier.php @@ -982,11 +982,12 @@ public function getCode($type, $code = '') } } - /** - * Return FeDex currency ISO code by Magento Base Currency Code - * - * @return string 3-digit currency code - */ + /** + * Return FeDex currency ISO code by Magento Base Currency Code + * + * @return string 3-digit currency code + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function getCurrencyCode() { $codes = [ @@ -1008,7 +1009,7 @@ public function getCurrencyCode() ]; $currencyCode = $this->_storeManager->getStore()->getBaseCurrencyCode(); - return isset($codes[$currencyCode]) ? $codes[$currencyCode] : $currencyCode; + return $codes[$currencyCode] ?? $currencyCode; } /** diff --git a/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationFactory.php b/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationFactory.php index cdca81ed98b7a..dbcb62907736c 100644 --- a/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationFactory.php +++ b/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationFactory.php @@ -72,6 +72,6 @@ public function create(VaultPaymentInterface $paymentMethod, int $storeId): Inte */ private function extractFromConfig($config, string $field, string $default): string { - return isset($config[$field]) ? $config[$field] : $default; + return $config[$field] ?? $default; } } diff --git a/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php b/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php index cdf7e0c6ac7c2..68090f12e99be 100644 --- a/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php +++ b/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php @@ -119,7 +119,7 @@ public function getName() */ public function getConfigData() { - return isset($this->data['config']) ? $this->data['config'] : []; + return $this->data['config'] ?? []; } /** diff --git a/app/code/Magento/Sales/Model/Order/Address.php b/app/code/Magento/Sales/Model/Order/Address.php index 77d8330a72550..eab671fdb85da 100644 --- a/app/code/Magento/Sales/Model/Order/Address.php +++ b/app/code/Magento/Sales/Model/Order/Address.php @@ -251,7 +251,7 @@ public function getStreet() public function getStreetLine($number) { $lines = $this->getStreet(); - return isset($lines[$number - 1]) ? $lines[$number - 1] : ''; + return $lines[$number - 1] ?? ''; } //@codeCoverageIgnoreStart diff --git a/app/code/Magento/Sales/Model/Order/Payment/Info.php b/app/code/Magento/Sales/Model/Order/Payment/Info.php index 063b3eaa71f1b..0a6b257846bfa 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Info.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Info.php @@ -181,7 +181,7 @@ public function getAdditionalInformation($key = null) if (null === $key) { return $this->additionalInformation; } - return isset($this->additionalInformation[$key]) ? $this->additionalInformation[$key] : null; + return $this->additionalInformation[$key] ?? null; } /** diff --git a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php index 8d8de47ba99cf..96312e7e0192b 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php @@ -472,7 +472,7 @@ public function getAdditionalInformation($key = null) $info = []; } if ($key) { - return isset($info[$key]) ? $info[$key] : null; + return $info[$key] ?? null; } return $info; } @@ -898,7 +898,7 @@ public function getTxnId() public function getHtmlTxnId() { $this->_eventManager->dispatch($this->_eventPrefix . '_html_txn_id', $this->_getEventData()); - return isset($this->_data['html_txn_id']) ? $this->_data['html_txn_id'] : $this->getTxnId(); + return $this->_data['html_txn_id'] ?? $this->getTxnId(); } /** diff --git a/app/code/Magento/Sales/Model/ResourceModel/AbstractGrid.php b/app/code/Magento/Sales/Model/ResourceModel/AbstractGrid.php index 87c5b917f6963..25c15449a9fb4 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/AbstractGrid.php +++ b/app/code/Magento/Sales/Model/ResourceModel/AbstractGrid.php @@ -103,6 +103,6 @@ protected function getLastUpdatedAtValue($default = '0000-00-00 00:00:00') $row = $this->getConnection()->fetchRow($select); - return isset($row['updated_at']) ? $row['updated_at'] : $default; + return $row['updated_at'] ?? $default; } } diff --git a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php index be2588dc48711..844258f80a284 100644 --- a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php +++ b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php @@ -184,11 +184,11 @@ public function setActiveFlag($code = 'active') /** * Return code of carrier * - * @return string + * @return string|null */ public function getCarrierCode() { - return isset($this->_code) ? $this->_code : null; + return $this->_code ?? null; } /** @@ -410,7 +410,7 @@ protected function _getCachedQuotes($requestParams) { $key = $this->_getQuotesCacheKey($requestParams); - return isset(self::$_quotesCache[$key]) ? self::$_quotesCache[$key] : null; + return self::$_quotesCache[$key] ?? null; } /** diff --git a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/AddressBuilder.php b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/AddressBuilder.php index f95968d4a1bf7..482f243f6f05d 100644 --- a/app/code/Magento/Signifyd/Model/SignifydGateway/Request/AddressBuilder.php +++ b/app/code/Magento/Signifyd/Model/SignifydGateway/Request/AddressBuilder.php @@ -41,6 +41,6 @@ private function getStreetLine($number, $street) { $lines = is_array($street) ? $street : []; - return isset($lines[$number - 1]) ? $lines[$number - 1] : ''; + return $lines[$number - 1] ?? ''; } } diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index af25957257421..47d89112c08ea 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -460,11 +460,12 @@ protected function _getSession() return $this->_session; } - /** - * Validation rules for store - * - * @return \Zend_Validate_Interface|null - */ + /** + * Validation rules for store + * + * @return \Zend_Validate_Interface|null + * @throws \Zend_Validate_Exception + */ protected function _getValidationRulesBeforeSave() { $validator = new \Magento\Framework\Validator\DataObject(); @@ -486,13 +487,15 @@ protected function _getValidationRulesBeforeSave() return $validator; } - /** - * Loading store data - * - * @param mixed $key - * @param string $field - * @return $this - */ + /** + * Loading store data + * + * @param mixed $key + * @param string $field + * + * @return $this + * @throws \Magento\Framework\Exception\LocalizedException + */ public function load($key, $field = null) { if (!is_numeric($key) && $field === null) { @@ -562,11 +565,12 @@ public function setWebsite(Website $website) $this->setWebsiteId($website->getId()); } - /** - * Retrieve store website - * - * @return Website|bool - */ + /** + * Retrieve store website + * + * @return Website|bool + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function getWebsite() { if ($this->getWebsiteId() === null) { @@ -575,13 +579,15 @@ public function getWebsite() return $this->websiteRepository->getById($this->getWebsiteId()); } - /** - * Retrieve url using store configuration specific - * - * @param string $route - * @param array $params - * @return string - */ + /** + * Retrieve url using store configuration specific + * + * @param string $route + * @param array $params + * + * @return string + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function getUrl($route = '', $params = []) { /** @var $url UrlInterface */ @@ -877,12 +883,14 @@ public function getDefaultCurrency() return $currency; } - /** - * Set current store currency code - * - * @param string $code - * @return string - */ + /** + * Set current store currency code + * + * @param string $code + * + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ public function setCurrentCurrencyCode($code) { $code = strtoupper($code); @@ -968,11 +976,12 @@ public function getAllowedCurrencies() return explode(',', $this->getConfig($this->_currencyInstalled)); } - /** - * Retrieve store current currency - * - * @return Currency - */ + /** + * Retrieve store current currency + * + * @return Currency + * @throws \Magento\Framework\Exception\LocalizedException + */ public function getCurrentCurrency() { $currency = $this->getData('current_currency'); @@ -990,21 +999,23 @@ public function getCurrentCurrency() return $currency; } - /** - * Retrieve current currency rate - * - * @return float - */ + /** + * Retrieve current currency rate + * + * @return float + * @throws \Magento\Framework\Exception\LocalizedException + */ public function getCurrentCurrencyRate() { return $this->getBaseCurrency()->getRate($this->getCurrentCurrency()); } - /** - * Retrieve root category identifier - * - * @return int - */ + /** + * Retrieve root category identifier + * + * @return int + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function getRootCategoryId() { if (!$this->getGroup()) { @@ -1025,11 +1036,12 @@ public function setGroup(Group $group) return $this; } - /** - * Retrieve group model - * - * @return Group|bool - */ + /** + * Retrieve group model + * + * @return Group|bool + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function getGroup() { if (null === $this->getGroupId()) { @@ -1132,11 +1144,12 @@ public function getDefaultGroupId() return $this->_getData('default_group_id'); } - /** - * Check if store can be deleted - * - * @return boolean - */ + /** + * Check if store can be deleted + * + * @return boolean + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function isCanDelete() { if (!$this->getId()) { @@ -1146,12 +1159,13 @@ public function isCanDelete() return $this->getGroup()->getStoresCount() > 1; } - /** - * Check if store is default - * - * @return boolean - * @since 100.1.0 - */ + /** + * Check if store is default + * + * @return boolean + * @since 100.1.0 + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function isDefault() { if (!$this->getId() && $this->getWebsite() && $this->getWebsite()->getStoresCount() == 0) { @@ -1160,14 +1174,16 @@ public function isDefault() return $this->getGroup()->getDefaultStoreId() == $this->getId(); } - /** - * Retrieve current url for store - * - * @param bool $fromStore - * @return string - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - */ + /** + * Retrieve current url for store + * + * @param bool $fromStore + * + * @return string + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function getCurrentUrl($fromStore = true) { $sidQueryParam = $this->_sidResolver->getSessionIdQueryParam($this->_getSession()); @@ -1238,22 +1254,24 @@ public function isActive() return (bool)$this->_getData('is_active'); } - /** - * Protect delete from non admin area - * - * @return $this - */ + /** + * Protect delete from non admin area + * + * @return $this + * @throws \Magento\Framework\Exception\LocalizedException + */ public function beforeDelete() { $this->_configDataResource->clearScopeData(ScopeInterface::SCOPE_STORES, $this->getId()); return parent::beforeDelete(); } - /** - * Rewrite in order to clear configuration cache - * - * @return $this - */ + /** + * Rewrite in order to clear configuration cache + * + * @return $this + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function afterDelete() { $store = $this; @@ -1308,11 +1326,12 @@ public function isReadOnly($value = null) return $this->_isReadOnly; } - /** - * Retrieve store group name - * - * @return string - */ + /** + * Retrieve store group name + * + * @return string + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function getFrontendName() { if (null === $this->_frontendName) { @@ -1349,7 +1368,7 @@ public function getIdentities() public function getStorePath() { $parsedUrl = parse_url($this->getBaseUrl()); - return isset($parsedUrl['path']) ? $parsedUrl['path'] : '/'; + return $parsedUrl['path'] ?? '/'; } /** diff --git a/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php b/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php index d7e3bf94f548f..a951954600365 100644 --- a/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php +++ b/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php @@ -45,14 +45,16 @@ protected function _construct() $this->_init('layout_update', 'layout_update_id'); } - /** - * Retrieve layout updates by handle - * - * @param string $handle - * @param \Magento\Framework\View\Design\ThemeInterface $theme - * @param \Magento\Framework\App\ScopeInterface $store - * @return string - */ + /** + * Retrieve layout updates by handle + * + * @param string $handle + * @param \Magento\Framework\View\Design\ThemeInterface $theme + * @param \Magento\Framework\App\ScopeInterface $store + * + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ public function fetchUpdatesByHandle( $handle, \Magento\Framework\View\Design\ThemeInterface $theme, @@ -69,15 +71,17 @@ public function fetchUpdatesByHandle( $this->layoutUpdateCache[$cacheKey][$layout['handle']] .= $layout['xml']; } } - return isset($this->layoutUpdateCache[$cacheKey][$handle]) ? $this->layoutUpdateCache[$cacheKey][$handle] : ''; + return $this->layoutUpdateCache[$cacheKey][$handle] ?? ''; } - /** - * Get select to fetch updates by handle - * - * @param bool $loadAllUpdates - * @return \Magento\Framework\DB\Select - */ + /** + * Get select to fetch updates by handle + * + * @param bool $loadAllUpdates + * + * @return \Magento\Framework\DB\Select + * @throws \Magento\Framework\Exception\LocalizedException + */ protected function _getFetchUpdatesByHandleSelect($loadAllUpdates = false) { //@todo Why it also loads layout updates for store_id=0, isn't it Admin Store View? From 699108bf3e27ba4c1b7e1588d2afa1b0f3899f3a Mon Sep 17 00:00:00 2001 From: Ruslan Kostiv Date: Thu, 20 Sep 2018 12:56:35 +0300 Subject: [PATCH 011/704] MAGETWO-95080: Add pagination to the customer addresses grid --- .../Magento/Customer/Block/Address/Book.php | 117 +++++++++++++++++- .../frontend/templates/address/book.phtml | 5 +- .../frontend/templates/order/history.phtml | 2 + .../web/css/source/_module.less | 5 +- 4 files changed, 123 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Customer/Block/Address/Book.php b/app/code/Magento/Customer/Block/Address/Book.php index e1c53356c7e69..2076d389bb563 100644 --- a/app/code/Magento/Customer/Block/Address/Book.php +++ b/app/code/Magento/Customer/Block/Address/Book.php @@ -43,6 +43,11 @@ class Book extends \Magento\Framework\View\Element\Template */ protected $addressMapper; + /** + * @var \Magento\Customer\Model\ResourceModel\Address\CollectionFactory + */ + private $addressesCollectionFactory; + /** * @param \Magento\Framework\View\Element\Template\Context $context * @param CustomerRepositoryInterface $customerRepository @@ -51,6 +56,7 @@ class Book extends \Magento\Framework\View\Element\Template * @param \Magento\Customer\Model\Address\Config $addressConfig * @param Mapper $addressMapper * @param array $data + * @param \Magento\Customer\Model\ResourceModel\Address\Collection $addressesCollection */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, @@ -59,13 +65,26 @@ public function __construct( \Magento\Customer\Helper\Session\CurrentCustomer $currentCustomer, \Magento\Customer\Model\Address\Config $addressConfig, Mapper $addressMapper, - array $data = [] + array $data = [], + \Magento\Customer\Model\ResourceModel\Address\CollectionFactory $addressesCollectionFactory, + \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor, + \Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface $collectionProcessor = null, + \Magento\Framework\Api\FilterBuilder $filterBuilder, + \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder ) { $this->customerRepository = $customerRepository; $this->currentCustomer = $currentCustomer; $this->addressRepository = $addressRepository; $this->_addressConfig = $addressConfig; $this->addressMapper = $addressMapper; + $this->addressesCollectionFactory = $addressesCollectionFactory; + $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor; + $this->collectionProcessor = $collectionProcessor ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + 'Magento\Eav\Model\Api\SearchCriteria\CollectionProcessor' + ); + $this->filterBuilder = $filterBuilder; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + parent::__construct($context, $data); } @@ -75,7 +94,20 @@ public function __construct( protected function _prepareLayout() { $this->pageConfig->getTitle()->set(__('Address Book')); - return parent::_prepareLayout(); + parent::_prepareLayout(); + if ($this->getAddresses()) { + $pager = $this->getLayout()->createBlock( + \Magento\Theme\Block\Html\Pager::class, + 'customer.addresses.pager' + ) + ->setCollection( + $this->getAddresses() + ) + ; + $this->setChild('pager', $pager); + $this->getAddresses()->load(); + } + return $this; } /** @@ -128,7 +160,10 @@ public function hasPrimaryAddress() public function getAdditionalAddresses() { try { - $addresses = $this->customerRepository->getById($this->currentCustomer->getCustomerId())->getAddresses(); + //$addresses = $this->customerRepository->getById($this->currentCustomer->getCustomerId())->getAddresses(); + + $addresses = $this->getAddresses(); + // continue work here!!! } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { return false; } @@ -224,4 +259,80 @@ public function getStreetAddress($street) } return $street; } + + /** + * @return string + */ + public function getPagerHtml() + { + return $this->getChildHtml('pager'); + } + + /** + * @var \Magento\Customer\Model\ResourceModel\Address\Collection + */ + private $addressesCollection; + + + /** + * @var \Magento\Customer\Model\ResourceModel\Address\CollectionFactory + */ + private $addressCollectionFactory; + + /** + * @var \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface + */ + private $extensionAttributesJoinProcessor; + + /** + * @var \Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface + */ + private $collectionProcessor; + + /** + * @var \Magento\Framework\Api\FilterBuilder + */ + private $filterBuilder; + + /** + * @var \Magento\Framework\Api\SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + private function getAddresses() + { + $customerId = $this->currentCustomer->getCustomerId(); + + if (!($customerId)) { + return false; + } + if (!$this->addressesCollection) { + + $filter = $this->filterBuilder->setField('parent_id') + ->setValue($customerId) + ->setConditionType('eq') + ->create(); + + + $searchCriteria = $this->searchCriteriaBuilder->addFilters([$filter])->create(); + + //$listtt = $this->addressRepository->getList($searchCriteria); + + /** @var \Magento\Customer\Model\ResourceModel\Address\Collection $collection */ + $collection = $this->addressesCollectionFactory->create(); + $this->extensionAttributesJoinProcessor->process( + $collection, + \Magento\Customer\Api\Data\AddressInterface::class + ); + + $this->collectionProcessor->process($searchCriteria, $collection); + $collection->setOrder( + 'created_at', + 'desc' + ); + $this->addressesCollection = $collection; + } + + return $this->addressesCollection; + } } diff --git a/app/code/Magento/Customer/view/frontend/templates/address/book.phtml b/app/code/Magento/Customer/view/frontend/templates/address/book.phtml index 96b2f4dd0a1e1..95e381455a293 100644 --- a/app/code/Magento/Customer/view/frontend/templates/address/book.phtml +++ b/app/code/Magento/Customer/view/frontend/templates/address/book.phtml @@ -79,7 +79,6 @@ - @@ -94,7 +93,6 @@ getStreetAddress($address->getStreet()) ?> getCity() ?> getCountryId() ?> - getRegion()->getRegion() ?> getPostcode() ?> getTelephone() ?> @@ -106,6 +104,9 @@
+ getPagerHtml()): ?> +
getPagerHtml() ?>
+

escapeHtml(__('You have no other address entries in your address book.')) ?>

diff --git a/app/code/Magento/Sales/view/frontend/templates/order/history.phtml b/app/code/Magento/Sales/view/frontend/templates/order/history.phtml index 1c02a5c31ea6b..b9a032212352b 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/history.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/history.phtml @@ -6,6 +6,8 @@ // @codingStandardsIgnoreFile +/** @var \Magento\Sales\Block\Order\History $block */ + ?> getOrders(); ?> getChildHtml('info') ?> diff --git a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less index d7ae6c3b28f4a..dfb9047595953 100755 --- a/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Customer/web/css/source/_module.less @@ -334,13 +334,16 @@ } } - .order-products-toolbar { + .order-products-toolbar, .customer-addresses-toolbar { position: relative; .toolbar-amount { position: relative; text-align: center; } + .pages { + position: relative; + } } } From 46fdf6db5b43313e197b4717ee51ca24cc6c2bd6 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi Date: Thu, 20 Sep 2018 22:00:41 +0300 Subject: [PATCH 012/704] Update Tests --- .../Catalog/Ui/Component/ColumnFactory.php | 18 +- .../Model/ObjectRegistry.php | 4 +- .../Block/Adminhtml/Edit/Tab/View/Sales.php | 18 +- app/code/Magento/Fedex/Model/Carrier.php | 12 +- app/code/Magento/Store/Model/Store.php | 206 +++++++++--------- .../Model/ResourceModel/Layout/Update.php | 36 +-- 6 files changed, 148 insertions(+), 146 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php index 8daec1fa6ca78..f400a699f7dde 100644 --- a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php +++ b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php @@ -46,14 +46,16 @@ public function __construct(\Magento\Framework\View\Element\UiComponentFactory $ $this->componentFactory = $componentFactory; } - /** - * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute - * @param \Magento\Framework\View\Element\UiComponent\ContextInterface $context - * @param array $config - * - * @return \Magento\Ui\Component\Listing\Columns\ColumnInterface - * @throws \Magento\Framework\Exception\LocalizedException - */ + /** + * Create Factory + * + * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute + * @param \Magento\Framework\View\Element\UiComponent\ContextInterface $context + * @param array $config + * + * @return \Magento\Ui\Component\Listing\Columns\ColumnInterface + * @throws \Magento\Framework\Exception\LocalizedException + */ public function create($attribute, $context, array $config = []) { $columnName = $attribute->getAttributeCode(); diff --git a/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php b/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php index ecc4ff04ab8a7..352948b8338bc 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php @@ -26,7 +26,7 @@ public function __construct($entities) } /** - * Get + * Get Entity * * @param int $entityId * @return \Magento\Framework\DataObject|null @@ -37,7 +37,7 @@ public function get($entityId) } /** - * List + * List Entities * * @return \Magento\Framework\DataObject[] */ diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Sales.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Sales.php index b9442797765c6..38732803aae63 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Sales.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Sales.php @@ -166,15 +166,15 @@ public function getTotals() return $this->_collection->getTotals(); } - /** - * Format price by specified website - * - * @param float $price - * @param null|int $websiteId - * - * @return string - * @throws \Magento\Framework\Exception\LocalizedException - */ + /** + * Format price by specified website + * + * @param float $price + * @param null|int $websiteId + * + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ public function formatCurrency($price, $websiteId = null) { return $this->_storeManager->getWebsite($websiteId)->getBaseCurrency()->format($price); diff --git a/app/code/Magento/Fedex/Model/Carrier.php b/app/code/Magento/Fedex/Model/Carrier.php index 843867e183cac..5a869d44b075d 100644 --- a/app/code/Magento/Fedex/Model/Carrier.php +++ b/app/code/Magento/Fedex/Model/Carrier.php @@ -982,12 +982,12 @@ public function getCode($type, $code = '') } } - /** - * Return FeDex currency ISO code by Magento Base Currency Code - * - * @return string 3-digit currency code - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ + /** + * Return FeDex currency ISO code by Magento Base Currency Code + * + * @return string 3-digit currency code + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function getCurrencyCode() { $codes = [ diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index 47d89112c08ea..d55bf0b5e1cd0 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -460,12 +460,12 @@ protected function _getSession() return $this->_session; } - /** - * Validation rules for store - * - * @return \Zend_Validate_Interface|null - * @throws \Zend_Validate_Exception - */ + /** + * Validation rules for store + * + * @return \Zend_Validate_Interface|null + * @throws \Zend_Validate_Exception + */ protected function _getValidationRulesBeforeSave() { $validator = new \Magento\Framework\Validator\DataObject(); @@ -487,15 +487,15 @@ protected function _getValidationRulesBeforeSave() return $validator; } - /** - * Loading store data - * - * @param mixed $key - * @param string $field - * - * @return $this - * @throws \Magento\Framework\Exception\LocalizedException - */ + /** + * Loading store data + * + * @param mixed $key + * @param string $field + * + * @return $this + * @throws \Magento\Framework\Exception\LocalizedException + */ public function load($key, $field = null) { if (!is_numeric($key) && $field === null) { @@ -565,12 +565,12 @@ public function setWebsite(Website $website) $this->setWebsiteId($website->getId()); } - /** - * Retrieve store website - * - * @return Website|bool - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ + /** + * Retrieve store website + * + * @return Website|bool + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function getWebsite() { if ($this->getWebsiteId() === null) { @@ -579,15 +579,15 @@ public function getWebsite() return $this->websiteRepository->getById($this->getWebsiteId()); } - /** - * Retrieve url using store configuration specific - * - * @param string $route - * @param array $params - * - * @return string - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ + /** + * Retrieve url using store configuration specific + * + * @param string $route + * @param array $params + * + * @return string + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function getUrl($route = '', $params = []) { /** @var $url UrlInterface */ @@ -883,14 +883,14 @@ public function getDefaultCurrency() return $currency; } - /** - * Set current store currency code - * - * @param string $code - * - * @return string - * @throws \Magento\Framework\Exception\LocalizedException - */ + /** + * Set current store currency code + * + * @param string $code + * + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ public function setCurrentCurrencyCode($code) { $code = strtoupper($code); @@ -976,12 +976,12 @@ public function getAllowedCurrencies() return explode(',', $this->getConfig($this->_currencyInstalled)); } - /** - * Retrieve store current currency - * - * @return Currency - * @throws \Magento\Framework\Exception\LocalizedException - */ + /** + * Retrieve store current currency + * + * @return Currency + * @throws \Magento\Framework\Exception\LocalizedException + */ public function getCurrentCurrency() { $currency = $this->getData('current_currency'); @@ -999,23 +999,23 @@ public function getCurrentCurrency() return $currency; } - /** - * Retrieve current currency rate - * - * @return float - * @throws \Magento\Framework\Exception\LocalizedException - */ + /** + * Retrieve current currency rate + * + * @return float + * @throws \Magento\Framework\Exception\LocalizedException + */ public function getCurrentCurrencyRate() { return $this->getBaseCurrency()->getRate($this->getCurrentCurrency()); } - /** - * Retrieve root category identifier - * - * @return int - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ + /** + * Retrieve root category identifier + * + * @return int + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function getRootCategoryId() { if (!$this->getGroup()) { @@ -1036,12 +1036,12 @@ public function setGroup(Group $group) return $this; } - /** - * Retrieve group model - * - * @return Group|bool - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ + /** + * Retrieve group model + * + * @return Group|bool + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function getGroup() { if (null === $this->getGroupId()) { @@ -1144,12 +1144,12 @@ public function getDefaultGroupId() return $this->_getData('default_group_id'); } - /** - * Check if store can be deleted - * - * @return boolean - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ + /** + * Check if store can be deleted + * + * @return boolean + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function isCanDelete() { if (!$this->getId()) { @@ -1159,13 +1159,13 @@ public function isCanDelete() return $this->getGroup()->getStoresCount() > 1; } - /** - * Check if store is default - * - * @return boolean - * @since 100.1.0 - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ + /** + * Check if store is default + * + * @return boolean + * @since 100.1.0 + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function isDefault() { if (!$this->getId() && $this->getWebsite() && $this->getWebsite()->getStoresCount() == 0) { @@ -1174,16 +1174,16 @@ public function isDefault() return $this->getGroup()->getDefaultStoreId() == $this->getId(); } - /** - * Retrieve current url for store - * - * @param bool $fromStore - * - * @return string - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ + /** + * Retrieve current url for store + * + * @param bool $fromStore + * + * @return string + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function getCurrentUrl($fromStore = true) { $sidQueryParam = $this->_sidResolver->getSessionIdQueryParam($this->_getSession()); @@ -1254,24 +1254,24 @@ public function isActive() return (bool)$this->_getData('is_active'); } - /** - * Protect delete from non admin area - * - * @return $this - * @throws \Magento\Framework\Exception\LocalizedException - */ + /** + * Protect delete from non admin area + * + * @return $this + * @throws \Magento\Framework\Exception\LocalizedException + */ public function beforeDelete() { $this->_configDataResource->clearScopeData(ScopeInterface::SCOPE_STORES, $this->getId()); return parent::beforeDelete(); } - /** - * Rewrite in order to clear configuration cache - * - * @return $this - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ + /** + * Rewrite in order to clear configuration cache + * + * @return $this + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function afterDelete() { $store = $this; @@ -1326,12 +1326,12 @@ public function isReadOnly($value = null) return $this->_isReadOnly; } - /** - * Retrieve store group name - * - * @return string - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ + /** + * Retrieve store group name + * + * @return string + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ public function getFrontendName() { if (null === $this->_frontendName) { diff --git a/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php b/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php index a951954600365..8817a947f835e 100644 --- a/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php +++ b/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php @@ -45,16 +45,16 @@ protected function _construct() $this->_init('layout_update', 'layout_update_id'); } - /** - * Retrieve layout updates by handle - * - * @param string $handle - * @param \Magento\Framework\View\Design\ThemeInterface $theme - * @param \Magento\Framework\App\ScopeInterface $store - * - * @return string - * @throws \Magento\Framework\Exception\LocalizedException - */ + /** + * Retrieve layout updates by handle + * + * @param string $handle + * @param \Magento\Framework\View\Design\ThemeInterface $theme + * @param \Magento\Framework\App\ScopeInterface $store + * + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ public function fetchUpdatesByHandle( $handle, \Magento\Framework\View\Design\ThemeInterface $theme, @@ -74,14 +74,14 @@ public function fetchUpdatesByHandle( return $this->layoutUpdateCache[$cacheKey][$handle] ?? ''; } - /** - * Get select to fetch updates by handle - * - * @param bool $loadAllUpdates - * - * @return \Magento\Framework\DB\Select - * @throws \Magento\Framework\Exception\LocalizedException - */ + /** + * Get select to fetch updates by handle + * + * @param bool $loadAllUpdates + * + * @return \Magento\Framework\DB\Select + * @throws \Magento\Framework\Exception\LocalizedException + */ protected function _getFetchUpdatesByHandleSelect($loadAllUpdates = false) { //@todo Why it also loads layout updates for store_id=0, isn't it Admin Store View? From 030b223efa3c7e5811ccddbf064d33fb7224c888 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun Date: Fri, 21 Sep 2018 15:06:05 +0300 Subject: [PATCH 013/704] Fix the issue with reset password when customer has address from not allowed country --- .../Customer/Model/AccountManagement.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index fe17adcb09c0d..95bc942355dfc 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -326,6 +326,11 @@ class AccountManagement implements AccountManagementInterface */ private $accountConfirmation; + /** + * @var AddressRegistry + */ + private $addressRegistry; + /** * @param CustomerFactory $customerFactory * @param ManagerInterface $eventManager @@ -356,6 +361,7 @@ class AccountManagement implements AccountManagementInterface * @param SessionManagerInterface|null $sessionManager * @param SaveHandlerInterface|null $saveHandler * @param CollectionFactory|null $visitorCollectionFactory + * @param AddressRegistry|null $addressRegistry * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -387,7 +393,8 @@ public function __construct( AccountConfirmation $accountConfirmation = null, SessionManagerInterface $sessionManager = null, SaveHandlerInterface $saveHandler = null, - CollectionFactory $visitorCollectionFactory = null + CollectionFactory $visitorCollectionFactory = null, + AddressRegistry $addressRegistry ) { $this->customerFactory = $customerFactory; $this->eventManager = $eventManager; @@ -423,6 +430,8 @@ public function __construct( ?: ObjectManager::getInstance()->get(SaveHandlerInterface::class); $this->visitorCollectionFactory = $visitorCollectionFactory ?: ObjectManager::getInstance()->get(CollectionFactory::class); + $this->addressRegistry = $addressRegistry + ?: ObjectManager::getInstance()->get(AddressRegistry::class); } /** @@ -568,6 +577,12 @@ public function initiatePasswordReset($email, $template, $websiteId = null) // load customer by email $customer = $this->customerRepository->get($email, $websiteId); + foreach ($customer->getAddresses() as $address) { + // No need to validate customer address while saving customer reset password token + $addressModel = $this->addressRegistry->retrieve($address->getId()); + $addressModel->setShouldIgnoreValidation(true); + } + $newPasswordToken = $this->mathRandom->getUniqueHash(); $this->changeResetPasswordLinkToken($customer, $newPasswordToken); From 54b6382b4b8c142e99afa62382c6adc729e82adb Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun Date: Sat, 22 Sep 2018 15:30:30 +0300 Subject: [PATCH 014/704] Updated the unit test, fix and additional issue #18170 --- .../Customer/Model/AccountManagement.php | 2 +- .../Test/Unit/Model/AccountManagementTest.php | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index 95bc942355dfc..a7db70d14fad1 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -394,7 +394,7 @@ public function __construct( SessionManagerInterface $sessionManager = null, SaveHandlerInterface $saveHandler = null, CollectionFactory $visitorCollectionFactory = null, - AddressRegistry $addressRegistry + AddressRegistry $addressRegistry = null ) { $this->customerFactory = $customerFactory; $this->eventManager = $eventManager; diff --git a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php index aad20f757e91e..0190ebcc4457e 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php @@ -142,6 +142,11 @@ class AccountManagementTest extends \PHPUnit\Framework\TestCase */ private $saveHandler; + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Customer\Model\AddressRegistry + */ + private $addressRegistryMock; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -176,6 +181,7 @@ protected function setUp() $this->dateTime = $this->createMock(\Magento\Framework\Stdlib\DateTime::class); $this->customer = $this->createMock(\Magento\Customer\Model\Customer::class); $this->objectFactory = $this->createMock(\Magento\Framework\DataObjectFactory::class); + $this->addressRegistryMock = $this->createMock(\Magento\Customer\Model\AddressRegistry::class); $this->extensibleDataObjectConverter = $this->createMock( \Magento\Framework\Api\ExtensibleDataObjectConverter::class ); @@ -239,6 +245,7 @@ protected function setUp() 'sessionManager' => $this->sessionManager, 'saveHandler' => $this->saveHandler, 'visitorCollectionFactory' => $this->visitorCollectionFactory, + 'addressRegistry' => $this->addressRegistryMock, ] ); $this->objectManagerHelper->setBackwardCompatibleProperty( @@ -1071,6 +1078,7 @@ public function testSendPasswordReminderEmail() protected function prepareInitiatePasswordReset($email, $templateIdentifier, $sender, $storeId, $customerId, $hash) { $websiteId = 1; + $addressId = 5; $datetime = $this->prepareDateTimeFactory(); @@ -1088,6 +1096,17 @@ protected function prepareInitiatePasswordReset($email, $templateIdentifier, $se ->method('getStore') ->willReturn($this->store); + /** @var \Magento\Customer\Model\Address|\PHPUnit_Framework_MockObject_MockObject $addressModel */ + $addressModel = $this->getMockBuilder(\Magento\Customer\Model\Address::class)->disableOriginalConstructor() + ->setMethods(['setShouldIgnoreValidation'])->getMock(); + + /** @var \Magento\Customer\Api\Data\AddressInterface|\PHPUnit_Framework_MockObject_MockObject $customer */ + $address = $this->createMock(\Magento\Customer\Api\Data\AddressInterface::class); + $address->expects($this->once()) + ->method('getId') + ->willReturn($addressId); + + /** @var \Magento\Customer\Api\Data\CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customer */ $customer = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) ->getMock(); $customer->expects($this->any()) @@ -1099,6 +1118,20 @@ protected function prepareInitiatePasswordReset($email, $templateIdentifier, $se $customer->expects($this->any()) ->method('getStoreId') ->willReturn($storeId); + $customer->expects($this->any()) + ->method('getAddresses') + ->willReturn([$address]); + + $this->customerRepository->expects($this->once()) + ->method('get') + ->willReturn($customer); + $this->addressRegistryMock->expects($this->once()) + ->method('retrieve') + ->with($addressId) + ->willReturn($addressModel); + $addressModel->expects($this->once()) + ->method('setShouldIgnoreValidation') + ->with(true); $this->customerRepository->expects($this->once()) ->method('get') From 1b5f7c5db19aec08fd832fe82a59e691ce2edf81 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi Date: Sun, 23 Sep 2018 01:02:47 +0300 Subject: [PATCH 015/704] fix tests --- .../Catalog/Ui/Component/ColumnFactory.php | 8 + .../Component/Listing/Columns/Thumbnail.php | 4 + .../Model/ObjectRegistry.php | 3 + .../Block/Adminhtml/Edit/Tab/View/Sales.php | 4 + .../Model/Address/AbstractAddress.php | 26 ++- app/code/Magento/Fedex/Model/Carrier.php | 18 +- .../IntegrationFactory.php | 2 +- .../DataProvider/NotificationDataProvider.php | 78 +++++++-- .../Magento/Sales/Model/Order/Address.php | 156 ++++++++++++++---- .../Sales/Model/Order/Payment/Info.php | 7 +- .../Sales/Model/Order/Payment/Transaction.php | 60 +++++-- .../Model/Carrier/AbstractCarrierOnline.php | 13 +- app/code/Magento/Store/Model/Store.php | 14 ++ .../Model/ResourceModel/Layout/Update.php | 4 +- 14 files changed, 317 insertions(+), 80 deletions(-) diff --git a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php index f400a699f7dde..1903bcd144831 100644 --- a/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php +++ b/app/code/Magento/Catalog/Ui/Component/ColumnFactory.php @@ -6,6 +6,8 @@ namespace Magento\Catalog\Ui\Component; /** + * Column Factory + * * @api * @since 100.0.2 */ @@ -86,7 +88,10 @@ public function create($attribute, $context, array $config = []) } /** + * Get Js Component + * * @param string $dataType + * * @return string */ protected function getJsComponent($dataType) @@ -95,7 +100,10 @@ protected function getJsComponent($dataType) } /** + * Get Data Type + * * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute + * * @return string */ protected function getDataType($attribute) diff --git a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php index dc3c94f384632..09c9782fc0e32 100644 --- a/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php +++ b/app/code/Magento/Catalog/Ui/Component/Listing/Columns/Thumbnail.php @@ -9,6 +9,8 @@ use Magento\Framework\View\Element\UiComponent\ContextInterface; /** + * Class Thumbnail + * * @api * @since 100.0.2 */ @@ -67,6 +69,8 @@ public function prepareDataSource(array $dataSource) } /** + * Get Alt + * * @param array $row * * @return null|string diff --git a/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php b/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php index 352948b8338bc..a048c216139e3 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/ObjectRegistry.php @@ -5,6 +5,9 @@ */ namespace Magento\CatalogUrlRewrite\Model; +/** + * Class ObjectRegistry + */ class ObjectRegistry { /** diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Sales.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Sales.php index 38732803aae63..5eeacca4c73a5 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Sales.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/View/Sales.php @@ -151,6 +151,8 @@ public function getWebsiteCount($websiteId) } /** + * Returns Grouped Collection Rows + * * @return array */ public function getRows() @@ -159,6 +161,8 @@ public function getRows() } /** + * Return totals data + * * @return \Magento\Framework\DataObject */ public function getTotals() diff --git a/app/code/Magento/Customer/Model/Address/AbstractAddress.php b/app/code/Magento/Customer/Model/Address/AbstractAddress.php index 2c0c7812ca669..caa2761a3dc3d 100644 --- a/app/code/Magento/Customer/Model/Address/AbstractAddress.php +++ b/app/code/Magento/Customer/Model/Address/AbstractAddress.php @@ -271,7 +271,8 @@ public function setStreet($street) * Enforce format of the street field or other multiline custom attributes * * @param array|string $key - * @param null $value + * @param array|string|null $value + * * @return \Magento\Framework\DataObject */ public function setData($key, $value = null) @@ -286,6 +287,7 @@ public function setData($key, $value = null) /** * Check that address can have multiline attribute by this code (as street or some custom attribute) + * * @param string $code * @return bool */ @@ -403,6 +405,8 @@ public function getRegionCode() } /** + * Return Region ID + * * @return int */ public function getRegionId() @@ -425,6 +429,8 @@ public function getRegionId() } /** + * Return Country + * * @return int */ public function getCountry() @@ -502,6 +508,8 @@ public function getConfig() } /** + * Processing object before save data + * * @return $this */ public function beforeSave() @@ -516,10 +524,12 @@ public function beforeSave() * * @param int|null $defaultBillingAddressId * @param int|null $defaultShippingAddressId + * * @return AddressInterface * Use Api/Data/AddressInterface as a result of service operations. Don't rely on the model to provide * the instance of Api/Data/AddressInterface * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @throws \Magento\Framework\Exception\LocalizedException */ public function getDataModel($defaultBillingAddressId = null, $defaultShippingAddressId = null) { @@ -591,6 +601,8 @@ public function validate() } /** + * Create Region Instance + * * @return \Magento\Directory\Model\Region */ protected function _createRegionInstance() @@ -599,6 +611,8 @@ protected function _createRegionInstance() } /** + * Create Country Instance + * * @return \Magento\Directory\Model\Country */ protected function _createCountryInstance() @@ -608,6 +622,7 @@ protected function _createCountryInstance() /** * Unset Region from address + * * @return $this * @since 100.2.0 */ @@ -617,8 +632,11 @@ public function unsRegion() } /** + * Is Company Required + * * @return bool * @since 100.2.0 + * @throws \Magento\Framework\Exception\LocalizedException */ protected function isCompanyRequired() { @@ -626,8 +644,11 @@ protected function isCompanyRequired() } /** + * Is Telephone Required + * * @return bool * @since 100.2.0 + * @throws \Magento\Framework\Exception\LocalizedException */ protected function isTelephoneRequired() { @@ -635,8 +656,11 @@ protected function isTelephoneRequired() } /** + * Is Fax Required + * * @return bool * @since 100.2.0 + * @throws \Magento\Framework\Exception\LocalizedException */ protected function isFaxRequired() { diff --git a/app/code/Magento/Fedex/Model/Carrier.php b/app/code/Magento/Fedex/Model/Carrier.php index 5a869d44b075d..955345851e67a 100644 --- a/app/code/Magento/Fedex/Model/Carrier.php +++ b/app/code/Magento/Fedex/Model/Carrier.php @@ -1439,6 +1439,8 @@ protected function _doShipmentRequest(\Magento\Framework\DataObject $request) } /** + * Return Tracking Number + * * @param array|object $trackingIds * @return string */ @@ -1453,10 +1455,10 @@ function ($val) { } /** - * For multi package shipments. Delete requested shipments if the current shipment - * request is failed + * For multi package shipments. Delete requested shipments if the current shipment request is failed * * @param array $data + * * @return bool */ public function rollBack($data) @@ -1476,6 +1478,7 @@ public function rollBack($data) * Return container types of carrier * * @param \Magento\Framework\DataObject|null $params + * * @return array|bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ @@ -1543,6 +1546,7 @@ public function getContainerTypesFilter() * Return delivery confirmation types of carrier * * @param \Magento\Framework\DataObject|null $params + * * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -1553,6 +1557,7 @@ public function getDeliveryConfirmationTypes(\Magento\Framework\DataObject $para /** * Recursive replace sensitive fields in debug data by the mask + * * @param string $data * @return string */ @@ -1570,6 +1575,7 @@ protected function filterDebugData($data) /** * Parse track details response from Fedex + * * @param \stdClass $trackInfo * @return array * @SuppressWarnings(PHPMD.CyclomaticComplexity) @@ -1640,6 +1646,7 @@ private function processTrackingDetails(\stdClass $trackInfo) /** * Parse delivery datetime from tracking details + * * @param \stdClass $trackInfo * @return \Datetime|null */ @@ -1656,8 +1663,7 @@ private function getDeliveryDateTime(\stdClass $trackInfo) } /** - * Get delivery address details in string representation - * Return City, State, Country Code + * Get delivery address details in string representation Return City, State, Country Code * * @param \stdClass $address * @return \Magento\Framework\Phrase|string @@ -1719,6 +1725,7 @@ private function processTrackDetailsEvents(array $events) /** * Append error message to rate result instance + * * @param string $trackingValue * @param string $errorMessage */ @@ -1760,8 +1767,7 @@ private function parseDate($timestamp) } /** - * Defines payment type by request. - * Two values are available: RECIPIENT or SENDER. + * Defines payment type by request. Two values are available: RECIPIENT or SENDER. * * @param DataObject $request * @return string diff --git a/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationFactory.php b/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationFactory.php index dbcb62907736c..0b698afea1de3 100644 --- a/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationFactory.php +++ b/app/code/Magento/InstantPurchase/PaymentMethodIntegration/IntegrationFactory.php @@ -65,7 +65,7 @@ public function create(VaultPaymentInterface $paymentMethod, int $storeId): Inte /** * Reads value from config. * - * @param $config + * @param array $config * @param string $field * @param string $default * @return string diff --git a/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php b/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php index 68090f12e99be..5cd5bf7f02bd8 100644 --- a/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php +++ b/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php @@ -82,7 +82,9 @@ public function __construct( } /** - * {@inheritdoc} + * Get data + * + * @return mixed */ public function getData() { @@ -95,7 +97,10 @@ public function getData() } /** - * {@inheritdoc} + * Get Meta + * + * @return array + * @throws \Magento\Framework\Exception\LocalizedException */ public function getMeta() { @@ -107,7 +112,9 @@ public function getMeta() } /** - * {@inheritdoc} + * Get Data Provider name + * + * @return string */ public function getName() { @@ -115,7 +122,9 @@ public function getName() } /** - * {@inheritdoc} + * Get config data + * + * @return mixed */ public function getConfigData() { @@ -123,7 +132,11 @@ public function getConfigData() } /** - * {@inheritdoc} + * Set config data + * + * @param mixed $config + * + * @return bool */ public function setConfigData($config) { @@ -133,7 +146,12 @@ public function setConfigData($config) } /** - * {@inheritdoc} + * Get Field Meta Info + * + * @param string $fieldSetName + * @param string $fieldName + * + * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getFieldMetaInfo($fieldSetName, $fieldName) @@ -142,7 +160,11 @@ public function getFieldMetaInfo($fieldSetName, $fieldName) } /** - * {@inheritdoc} + * Get Field Set Meta Info + * + * @param string $fieldSetName + * + * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getFieldSetMetaInfo($fieldSetName) @@ -151,7 +173,11 @@ public function getFieldSetMetaInfo($fieldSetName) } /** - * {@inheritdoc} + * Get Fields Meta Info + * + * @param string $fieldSetName + * + * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getFieldsMetaInfo($fieldSetName) @@ -160,7 +186,9 @@ public function getFieldsMetaInfo($fieldSetName) } /** - * {@inheritdoc} + * Get Primary Field Name + * + * @return string */ public function getPrimaryFieldName() { @@ -168,7 +196,9 @@ public function getPrimaryFieldName() } /** - * {@inheritdoc} + * Get Request Field Name + * + * @return string */ public function getRequestFieldName() { @@ -176,7 +206,11 @@ public function getRequestFieldName() } /** - * {@inheritdoc} + * Add Filter + * + * @param \Magento\Framework\Api\Filter $filter + * + * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function addFilter(\Magento\Framework\Api\Filter $filter) @@ -184,7 +218,12 @@ public function addFilter(\Magento\Framework\Api\Filter $filter) } /** - * {@inheritdoc} + * Add Order + * + * @param string $field + * @param string $direction + * + * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function addOrder($field, $direction) @@ -192,7 +231,12 @@ public function addOrder($field, $direction) } /** - * {@inheritdoc} + * Set Limit + * + * @param int $offset + * @param int $size + * + * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function setLimit($offset, $size) @@ -200,7 +244,9 @@ public function setLimit($offset, $size) } /** - * {@inheritdoc} + * Get Search Criteria + * + * @return SearchCriteriaInterface */ public function getSearchCriteria() { @@ -208,7 +254,9 @@ public function getSearchCriteria() } /** - * {@inheritdoc} + * Get Search Result + * + * @return SearchResultInterface */ public function getSearchResult() { diff --git a/app/code/Magento/Sales/Model/Order/Address.php b/app/code/Magento/Sales/Model/Order/Address.php index eab671fdb85da..a525d62662d03 100644 --- a/app/code/Magento/Sales/Model/Order/Address.php +++ b/app/code/Magento/Sales/Model/Order/Address.php @@ -173,7 +173,7 @@ protected function implodeStreetValue($value) * Enforce format of the street field * * @param array|string $key - * @param null $value + * @param array|string $value * @return \Magento\Framework\DataObject */ public function setData($key, $value = null) @@ -508,7 +508,11 @@ public function getVatRequestSuccess() } /** - * {@inheritdoc} + * Set Parent ID + * + * @param int $id + * + * @return \Magento\Framework\DataObject|Address */ public function setParentId($id) { @@ -516,7 +520,11 @@ public function setParentId($id) } /** - * {@inheritdoc} + * Sets the country address ID for the order address. + * + * @param int $id + * + * @return \Magento\Framework\DataObject|Address */ public function setCustomerAddressId($id) { @@ -524,7 +532,11 @@ public function setCustomerAddressId($id) } /** - * {@inheritdoc} + * Sets the region ID for the order address. + * + * @param int $id + * + * @return \Magento\Framework\DataObject|Address */ public function setRegionId($id) { @@ -532,7 +544,11 @@ public function setRegionId($id) } /** - * {@inheritdoc} + * Sets the customer ID for the order address. + * + * @param int $id + * + * @return \Magento\Framework\DataObject|Address */ public function setStreet($street) { @@ -540,7 +556,11 @@ public function setStreet($street) } /** - * {@inheritdoc} + * Sets the fax number for the order address. + * + * @param string $fax + * + * @return \Magento\Framework\DataObject|Address */ public function setCustomerId($id) { @@ -548,7 +568,11 @@ public function setCustomerId($id) } /** - * {@inheritdoc} + * Sets the region for the order address. + * + * @param string $region + * + * @return \Magento\Framework\DataObject|Address */ public function setFax($fax) { @@ -556,7 +580,11 @@ public function setFax($fax) } /** - * {@inheritdoc} + * Sets the postal code for the order address. + * + * @param string $postcode + * + * @return \Magento\Framework\DataObject|Address */ public function setRegion($region) { @@ -564,7 +592,11 @@ public function setRegion($region) } /** - * {@inheritdoc} + * Sets the last name for the order address. + * + * @param string $lastname + * + * @return \Magento\Framework\DataObject|Address */ public function setPostcode($postcode) { @@ -572,7 +604,11 @@ public function setPostcode($postcode) } /** - * {@inheritdoc} + * Sets the street values, if any, for the order address. + * + * @param string|string[] $street + * + * @return \Magento\Framework\DataObject|Address */ public function setLastname($lastname) { @@ -580,7 +616,11 @@ public function setLastname($lastname) } /** - * {@inheritdoc} + * Sets the city for the order address. + * + * @param string $city + * + * @return \Magento\Framework\DataObject|Address */ public function setCity($city) { @@ -588,7 +628,11 @@ public function setCity($city) } /** - * {@inheritdoc} + * Sets the email address for the order address. + * + * @param string $email + * + * @return \Magento\Framework\DataObject|Address */ public function setEmail($email) { @@ -596,7 +640,11 @@ public function setEmail($email) } /** - * {@inheritdoc} + * Sets the telephone number for the order address. + * + * @param string $telephone + * + * @return \Magento\Framework\DataObject|Address */ public function setTelephone($telephone) { @@ -604,7 +652,11 @@ public function setTelephone($telephone) } /** - * {@inheritdoc} + * Sets the country ID for the order address. + * + * @param string $id + * + * @return \Magento\Framework\DataObject|Address */ public function setCountryId($id) { @@ -612,7 +664,11 @@ public function setCountryId($id) } /** - * {@inheritdoc} + * Sets the first name for the order address. + * + * @param string $firstname + * + * @return \Magento\Framework\DataObject|Address */ public function setFirstname($firstname) { @@ -620,7 +676,11 @@ public function setFirstname($firstname) } /** - * {@inheritdoc} + * Sets the address type for the order address. + * + * @param string $addressType + * + * @return \Magento\Framework\DataObject|Address */ public function setAddressType($addressType) { @@ -628,7 +688,11 @@ public function setAddressType($addressType) } /** - * {@inheritdoc} + * Sets the prefix for the order address. + * + * @param string $prefix + * + * @return \Magento\Framework\DataObject|Address */ public function setPrefix($prefix) { @@ -636,7 +700,11 @@ public function setPrefix($prefix) } /** - * {@inheritdoc} + * Sets the middle name for the order address. + * + * @param string $middlename + * + * @return \Magento\Framework\DataObject|Address */ public function setMiddlename($middlename) { @@ -644,7 +712,11 @@ public function setMiddlename($middlename) } /** - * {@inheritdoc} + * Sets the suffix for the order address. + * + * @param string $suffix + * + * @return \Magento\Framework\DataObject|Address */ public function setSuffix($suffix) { @@ -652,7 +724,11 @@ public function setSuffix($suffix) } /** - * {@inheritdoc} + * Sets the company for the order address. + * + * @param string $company + * + * @return \Magento\Framework\DataObject|Address */ public function setCompany($company) { @@ -660,7 +736,11 @@ public function setCompany($company) } /** - * {@inheritdoc} + * Sets the VAT ID for the order address. + * + * @param string $id + * + * @return \Magento\Framework\DataObject|Address */ public function setVatId($id) { @@ -668,7 +748,11 @@ public function setVatId($id) } /** - * {@inheritdoc} + * Sets the VAT-is-valid flag value for the order address. + * + * @param int $vatIsValid + * + * @return \Magento\Framework\DataObject|Address */ public function setVatIsValid($vatIsValid) { @@ -676,7 +760,11 @@ public function setVatIsValid($vatIsValid) } /** - * {@inheritdoc} + * Sets the VAT request ID for the order address. + * + * @param string $id + * + * @return \Magento\Framework\DataObject|Address */ public function setVatRequestId($id) { @@ -684,7 +772,11 @@ public function setVatRequestId($id) } /** - * {@inheritdoc} + * Set region code + * + * @param string $regionCode + * + * @return \Magento\Framework\DataObject|Address */ public function setRegionCode($regionCode) { @@ -692,7 +784,11 @@ public function setRegionCode($regionCode) } /** - * {@inheritdoc} + * Sets the VAT request date for the order address. + * + * @param string $vatRequestDate + * + * @return \Magento\Framework\DataObject|Address */ public function setVatRequestDate($vatRequestDate) { @@ -700,7 +796,11 @@ public function setVatRequestDate($vatRequestDate) } /** - * {@inheritdoc} + * Sets the VAT-request-success flag value for the order address. + * + * @param int $vatRequestSuccess + * + * @return \Magento\Framework\DataObject|Address */ public function setVatRequestSuccess($vatRequestSuccess) { @@ -708,7 +808,7 @@ public function setVatRequestSuccess($vatRequestSuccess) } /** - * {@inheritdoc} + * Retrieve existing extension attributes object or create a new one. * * @return \Magento\Sales\Api\Data\OrderAddressExtensionInterface|null */ @@ -718,7 +818,7 @@ public function getExtensionAttributes() } /** - * {@inheritdoc} + * Set an extension attributes object. * * @param \Magento\Sales\Api\Data\OrderAddressExtensionInterface $extensionAttributes * @return $this diff --git a/app/code/Magento/Sales/Model/Order/Payment/Info.php b/app/code/Magento/Sales/Model/Order/Payment/Info.php index 0a6b257846bfa..fee846fe6a62c 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Info.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Info.php @@ -11,8 +11,8 @@ use Magento\Payment\Model\InfoInterface; /** - * * Payment information model + * * @api * @since 100.0.2 */ @@ -41,7 +41,7 @@ class Info extends AbstractModel implements InfoInterface * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory - * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory, + * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory * @param \Magento\Payment\Helper\Data $paymentData * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource @@ -219,8 +219,7 @@ public function hasAdditionalInformation($key = null) } /** - * Initialize additional information container with data from model - * if property empty + * Initialize additional information container with data from model if property empty * * @return void */ diff --git a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php index 96312e7e0192b..07d9dfc851cf6 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php @@ -132,10 +132,12 @@ class Transaction extends AbstractModel implements TransactionInterface * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory * @param AttributeValueFactory $customAttributeFactory * @param \Magento\Sales\Model\OrderFactory $orderFactory + * @param \Magento\Sales\Api\OrderPaymentRepositoryInterface $orderPaymentRepository + * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository * @param \Magento\Framework\Stdlib\DateTime\DateTimeFactory $dateFactory * @param TransactionFactory $transactionFactory - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource + * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $data * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -193,8 +195,7 @@ public function setTxnId($txnId) } /** - * Parent transaction ID setter - * Can set the transaction id as well + * Parent transaction ID setter Can set the transaction id as well * * @param string $parentTxnId * @param string $txnId @@ -229,8 +230,7 @@ public function setTxnType($txnType) } /** - * Parent transaction getter - * May attempt to load it. + * Parent transaction getter. May attempt to load it. * * @param bool $shouldLoad * @return bool|\Magento\Sales\Model\Order\Payment\Transaction @@ -366,8 +366,7 @@ public function closeAuthorization($shouldSave = true, $dryRun = false) } /** - * Close a capture transaction - * Logic is similar to closeAuthorization(), but for a capture transaction + * Close a capture transaction. Logic is similar to closeAuthorization(), but for a capture transaction * * @param bool $shouldSave * @return bool|\Magento\Sales\Model\Order\Payment\Transaction @@ -395,6 +394,7 @@ public function closeCapture($shouldSave = true) /** * Check whether authorization in current hierarchy can be voided completely + * * Basically checks whether the authorization exists and it is not affected by a capture or void * * @return bool @@ -536,6 +536,7 @@ public function close($shouldSave = true) /** * Order ID getter + * * Attempts to get ID from set order payment object, if any, or from data by key 'order_id' * * @return int|null @@ -572,6 +573,7 @@ public function getOrder() /** * Set order instance for transaction depends on transaction behavior + * * If $order equals to true, method isn't loading new order instance. * * @param \Magento\Sales\Model\Order|null|boolean $order @@ -695,7 +697,6 @@ protected function _loadChildren() /** * Check whether this transaction is voided * - * TODO: implement that there should be only one void per authorization * @return bool */ protected function _isVoided() @@ -805,7 +806,6 @@ protected function _verifyTxnId($txnId) /** * Make sure this object is a valid transaction - * TODO for more restriction we can check for data consistency * * @return void * @throws \Magento\Framework\Exception\LocalizedException @@ -833,7 +833,11 @@ public function getTransactionId() } /** - * {@inheritdoc} + * Set Transaction ID + * + * @param int $id + * + * @return TransactionInterface|Transaction */ public function setTransactionId($id) { @@ -942,7 +946,11 @@ public function getCreatedAt() } /** - * {@inheritdoc} + * Gets an array of child transactions for the transaction. + * + * @param string $createdAt + * + * @return TransactionInterface|Transaction */ public function setCreatedAt($createdAt) { @@ -950,7 +958,11 @@ public function setCreatedAt($createdAt) } /** - * {@inheritdoc} + * Sets the parent ID for the transaction. + * + * @param int $id + * + * @return TransactionInterface|Transaction */ public function setParentId($id) { @@ -958,7 +970,11 @@ public function setParentId($id) } /** - * {@inheritdoc} + * Sets the order ID for the transaction. + * + * @param int $id + * + * @return TransactionInterface|Transaction */ public function setOrderId($id) { @@ -966,7 +982,11 @@ public function setOrderId($id) } /** - * {@inheritdoc} + * Sets the payment ID for the transaction. + * + * @param int $id + * + * @return TransactionInterface|Transaction */ public function setPaymentId($id) { @@ -974,7 +994,11 @@ public function setPaymentId($id) } /** - * {@inheritdoc} + * Sets the parent transaction business ID for the transaction. + * + * @param string $id + * + * @return TransactionInterface|Transaction */ public function setIsClosed($isClosed) { @@ -982,7 +1006,7 @@ public function setIsClosed($isClosed) } /** - * {@inheritdoc} + * Retrieve existing extension attributes object or create a new one. * * @return \Magento\Sales\Api\Data\TransactionExtensionInterface|null */ @@ -992,7 +1016,7 @@ public function getExtensionAttributes() } /** - * {@inheritdoc} + * Set an extension attributes object. * * @param \Magento\Sales\Api\Data\TransactionExtensionInterface $extensionAttributes * @return $this diff --git a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php index 844258f80a284..27047ae46bf1f 100644 --- a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php +++ b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php @@ -216,6 +216,7 @@ public function getTrackingInfo($tracking) /** * Check if carrier has shipping tracking option available + * * All \Magento\Usa carriers have shipping tracking option available * * @return boolean @@ -399,8 +400,8 @@ protected function _getQuotesCacheKey($requestParams) /** * Checks whether some request to rates have already been done, so we have cache for it - * Used to reduce number of same requests done to carrier service during one session * + * Used to reduce number of same requests done to carrier service during one session * Returns cached response or null * * @param string|array $requestParams @@ -443,8 +444,7 @@ protected function _prepareServiceName($name) } /** - * Prepare shipment request. - * Validate and correct request information + * Prepare shipment request. Validate and correct request information * * @param \Magento\Framework\DataObject $request * @return void @@ -558,8 +558,7 @@ public function returnOfShipment($request) } /** - * For multi package shipments. Delete requested shipments if the current shipment - * request is failed + * For multi package shipments. Delete requested shipments if the current shipment. Request is failed * * @param array $data * @return bool @@ -625,6 +624,8 @@ public function isGirthAllowed($countyDest = null, $carrierMethodCode = null) } /** + * Set Raw Request + * * @param \Magento\Framework\DataObject|null $request * @return $this * @api @@ -680,6 +681,7 @@ public function parseXml($xmlContent, $customSimplexml = 'SimpleXMLElement') /** * Checks if shipping method can collect rates + * * @return bool */ public function canCollectRates() @@ -689,6 +691,7 @@ public function canCollectRates() /** * Debug errors if showmethod is unset + * * @param Error $errors * * @return void diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index d55bf0b5e1cd0..e64f5f21bd5eb 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -412,6 +412,8 @@ public function __construct( } /** + * Magic method called during class serialization + * * @return string[] */ public function __sleep() @@ -792,6 +794,8 @@ public function isFrontUrlSecure() } /** + * Return is URL should be secure + * * @return bool */ public function isUrlSecure() @@ -1363,6 +1367,8 @@ public function getIdentities() } /** + * Return Store Path + * * @return string */ public function getStorePath() @@ -1372,6 +1378,8 @@ public function getStorePath() } /** + * Get scope type + * * {@inheritdoc} * @since 100.1.0 */ @@ -1381,6 +1389,8 @@ public function getScopeType() } /** + * Get Scope Type Name + * * {@inheritdoc} * @since 100.1.0 */ @@ -1390,6 +1400,8 @@ public function getScopeTypeName() } /** + * Retrieve existing extension attributes object or create a new one. + * * {@inheritdoc} */ public function getExtensionAttributes() @@ -1398,6 +1410,8 @@ public function getExtensionAttributes() } /** + * Set an extension attributes object. + * * @param \Magento\Store\Api\Data\StoreExtensionInterface $extensionAttributes * @return $this */ diff --git a/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php b/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php index 8817a947f835e..4333b2cf4fe60 100644 --- a/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php +++ b/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php @@ -48,9 +48,9 @@ protected function _construct() /** * Retrieve layout updates by handle * - * @param string $handle + * @param string $handle * @param \Magento\Framework\View\Design\ThemeInterface $theme - * @param \Magento\Framework\App\ScopeInterface $store + * @param \Magento\Framework\App\ScopeInterface store * * @return string * @throws \Magento\Framework\Exception\LocalizedException From 7aeb3be0530da6e3641b432ec2a5a5d99d43635c Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi Date: Sun, 23 Sep 2018 12:24:03 +0300 Subject: [PATCH 016/704] fix tests --- .../DataProvider/NotificationDataProvider.php | 2 +- .../Magento/Sales/Model/Order/Address.php | 24 +++++++++---------- .../Sales/Model/Order/Payment/Transaction.php | 4 ++-- app/code/Magento/Store/Model/Store.php | 6 ++--- .../Model/ResourceModel/Layout/Update.php | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php b/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php index 5cd5bf7f02bd8..26e698b67545b 100644 --- a/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php +++ b/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php @@ -147,7 +147,7 @@ public function setConfigData($config) /** * Get Field Meta Info - * + * * @param string $fieldSetName * @param string $fieldName * diff --git a/app/code/Magento/Sales/Model/Order/Address.php b/app/code/Magento/Sales/Model/Order/Address.php index a525d62662d03..8a4406e17a7e6 100644 --- a/app/code/Magento/Sales/Model/Order/Address.php +++ b/app/code/Magento/Sales/Model/Order/Address.php @@ -544,9 +544,9 @@ public function setRegionId($id) } /** - * Sets the customer ID for the order address. + * Sets the street values, if any, for the order address. * - * @param int $id + * @param string|string[] $street * * @return \Magento\Framework\DataObject|Address */ @@ -556,9 +556,9 @@ public function setStreet($street) } /** - * Sets the fax number for the order address. + * Sets the customer ID for the order address. * - * @param string $fax + * @param int $id * * @return \Magento\Framework\DataObject|Address */ @@ -568,9 +568,9 @@ public function setCustomerId($id) } /** - * Sets the region for the order address. + * Sets the fax number for the order address. * - * @param string $region + * @param string $fax * * @return \Magento\Framework\DataObject|Address */ @@ -580,9 +580,9 @@ public function setFax($fax) } /** - * Sets the postal code for the order address. + * Sets the region for the order address. * - * @param string $postcode + * @param string $region * * @return \Magento\Framework\DataObject|Address */ @@ -592,9 +592,9 @@ public function setRegion($region) } /** - * Sets the last name for the order address. + * Sets the postal code for the order address. * - * @param string $lastname + * @param string $postcode * * @return \Magento\Framework\DataObject|Address */ @@ -604,9 +604,9 @@ public function setPostcode($postcode) } /** - * Sets the street values, if any, for the order address. + * Sets the last name for the order address. * - * @param string|string[] $street + * @param string $lastname * * @return \Magento\Framework\DataObject|Address */ diff --git a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php index 07d9dfc851cf6..d1a8e99a98027 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php @@ -994,9 +994,9 @@ public function setPaymentId($id) } /** - * Sets the parent transaction business ID for the transaction. + * Sets the value of the is-closed flag for the transaction. * - * @param string $id + * @param int $isClosed * * @return TransactionInterface|Transaction */ diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index e64f5f21bd5eb..31d2b7bf30327 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -1380,7 +1380,7 @@ public function getStorePath() /** * Get scope type * - * {@inheritdoc} + * @return string * @since 100.1.0 */ public function getScopeType() @@ -1391,7 +1391,7 @@ public function getScopeType() /** * Get Scope Type Name * - * {@inheritdoc} + * @return string * @since 100.1.0 */ public function getScopeTypeName() @@ -1402,7 +1402,7 @@ public function getScopeTypeName() /** * Retrieve existing extension attributes object or create a new one. * - * {@inheritdoc} + * @return \Magento\Store\Api\Data\StoreExtensionInterface|null */ public function getExtensionAttributes() { diff --git a/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php b/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php index 4333b2cf4fe60..6c0aef9e67186 100644 --- a/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php +++ b/app/code/Magento/Widget/Model/ResourceModel/Layout/Update.php @@ -50,7 +50,7 @@ protected function _construct() * * @param string $handle * @param \Magento\Framework\View\Design\ThemeInterface $theme - * @param \Magento\Framework\App\ScopeInterface store + * @param \Magento\Framework\App\ScopeInterface $store * * @return string * @throws \Magento\Framework\Exception\LocalizedException From cdd19a602f03b0da45bf36e6ea634ec2bae5be29 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi Date: Tue, 25 Sep 2018 12:55:32 +0300 Subject: [PATCH 017/704] Update Comments --- app/code/Magento/Sales/Model/Order/Address.php | 6 +----- app/code/Magento/Sales/Model/Order/Payment/Transaction.php | 6 +----- app/code/Magento/Store/Model/Store.php | 5 +---- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Address.php b/app/code/Magento/Sales/Model/Order/Address.php index 8a4406e17a7e6..98433899cca26 100644 --- a/app/code/Magento/Sales/Model/Order/Address.php +++ b/app/code/Magento/Sales/Model/Order/Address.php @@ -508,11 +508,7 @@ public function getVatRequestSuccess() } /** - * Set Parent ID - * - * @param int $id - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setParentId($id) { diff --git a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php index d1a8e99a98027..8f9f275de1658 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php @@ -833,11 +833,7 @@ public function getTransactionId() } /** - * Set Transaction ID - * - * @param int $id - * - * @return TransactionInterface|Transaction + * @inheritdoc */ public function setTransactionId($id) { diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index 31d2b7bf30327..c212ebfec845f 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -1378,10 +1378,7 @@ public function getStorePath() } /** - * Get scope type - * - * @return string - * @since 100.1.0 + * @inheritdoc */ public function getScopeType() { From 23666acfbe35a00fe5c5be2c1e0e95440f32310f Mon Sep 17 00:00:00 2001 From: Yevhenii Dumskyi Date: Tue, 25 Sep 2018 22:00:22 +0300 Subject: [PATCH 018/704] Add Catalog search action handle if no results --- .../Controller/Advanced/Result.php | 25 +++++++++- .../CatalogSearch/Controller/Result/Index.php | 23 ++++++--- .../Unit/Controller/Advanced/ResultTest.php | 49 ++++++++++++++++++- 3 files changed, 89 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php b/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php index 2862efe4b4cd3..78890643c59fb 100644 --- a/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php +++ b/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php @@ -11,11 +11,15 @@ use Magento\Framework\UrlFactory; /** + * Catalog advanced search result + * * @deprecated CatalogSearch will be removed in 2.4, and {@see \Magento\ElasticSearch} * will replace it as the default search engine. */ class Result extends \Magento\Framework\App\Action\Action { + const DEFAULT_NO_RESULT_HANDLE = 'catalogsearch_advanced_result_noresults'; + /** * Url factory * @@ -48,13 +52,22 @@ public function __construct( } /** + * Run action + * * @return \Magento\Framework\Controller\Result\Redirect */ public function execute() { try { $this->_catalogSearchAdvanced->addFilters($this->getRequest()->getQueryValue()); - $this->_view->loadLayout(); + $size = $this->_catalogSearchAdvanced->getProductCollection()->getSize(); + + $handles = null; + if ($size == 0) { + $handles = [static::DEFAULT_NO_RESULT_HANDLE]; + } + + $this->_view->loadLayout($handles); $this->_view->renderLayout(); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addError($e->getMessage()); @@ -66,4 +79,14 @@ public function execute() return $resultRedirect; } } + + /** + * Returns no result handle + * + * @return string + */ + private function getNoResultsHandle() + { + return self::DEFAULT_NO_RESULT_HANDLE; + } } diff --git a/app/code/Magento/CatalogSearch/Controller/Result/Index.php b/app/code/Magento/CatalogSearch/Controller/Result/Index.php index ab796e12d81d9..3e0569ef7bfe8 100644 --- a/app/code/Magento/CatalogSearch/Controller/Result/Index.php +++ b/app/code/Magento/CatalogSearch/Controller/Result/Index.php @@ -14,11 +14,15 @@ use Magento\Search\Model\PopularSearchTerms; /** + * Catalog search result + * * @deprecated CatalogSearch will be removed in 2.4, and {@see \Magento\ElasticSearch} * will replace it as the default search engine. */ class Index extends \Magento\Framework\App\Action\Action { + const DEFAULT_NO_RESULT_HANDLE = 'catalogsearch_result_index_noresults'; + /** * Catalog session * @@ -89,12 +93,17 @@ public function execute() $getAdditionalRequestParameters = $this->getRequest()->getParams(); unset($getAdditionalRequestParameters[QueryFactory::QUERY_VAR_NAME]); + $handles = null; + if ($query->getNumResults() == 0) { + $handles = [static::DEFAULT_NO_RESULT_HANDLE]; + } + if (empty($getAdditionalRequestParameters) && $this->_objectManager->get(PopularSearchTerms::class)->isCacheable($queryText, $storeId) ) { - $this->getCacheableResult($catalogSearchHelper, $query); + $this->getCacheableResult($catalogSearchHelper, $query, $handles); } else { - $this->getNotCacheableResult($catalogSearchHelper, $query); + $this->getNotCacheableResult($catalogSearchHelper, $query, $handles); } } else { $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl()); @@ -106,9 +115,10 @@ public function execute() * * @param \Magento\CatalogSearch\Helper\Data $catalogSearchHelper * @param \Magento\Search\Model\Query $query + * @param array $handles * @return void */ - private function getCacheableResult($catalogSearchHelper, $query) + private function getCacheableResult($catalogSearchHelper, $query, $handles) { if (!$catalogSearchHelper->isMinQueryLength()) { $redirect = $query->getRedirect(); @@ -120,7 +130,7 @@ private function getCacheableResult($catalogSearchHelper, $query) $catalogSearchHelper->checkNotes(); - $this->_view->loadLayout(); + $this->_view->loadLayout($handles); $this->_view->renderLayout(); } @@ -129,11 +139,12 @@ private function getCacheableResult($catalogSearchHelper, $query) * * @param \Magento\CatalogSearch\Helper\Data $catalogSearchHelper * @param \Magento\Search\Model\Query $query + * @param array $handles * @return void * * @throws \Magento\Framework\Exception\LocalizedException */ - private function getNotCacheableResult($catalogSearchHelper, $query) + private function getNotCacheableResult($catalogSearchHelper, $query, $handles) { if ($catalogSearchHelper->isMinQueryLength()) { $query->setId(0)->setIsActive(1)->setIsProcessed(1); @@ -148,7 +159,7 @@ private function getNotCacheableResult($catalogSearchHelper, $query) $catalogSearchHelper->checkNotes(); - $this->_view->loadLayout(); + $this->_view->loadLayout($handles); $this->getResponse()->setNoCacheHeaders(); $this->_view->renderLayout(); } diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php index 891f008979e17..d4229ff934e9b 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php @@ -27,9 +27,15 @@ function () use (&$filters, $expectedQuery) { $request = $this->createPartialMock(\Magento\Framework\App\Console\Request::class, ['getQueryValue']); $request->expects($this->once())->method('getQueryValue')->will($this->returnValue($expectedQuery)); + $collection = $this->createPartialMock( + \Magento\CatalogSearch\Model\ResourceModel\Advanced\Collection::class, + ['getSize'] + ); + $collection->expects($this->once())->method('getSize')->will($this->returnValue(1)); + $catalogSearchAdvanced = $this->createPartialMock( \Magento\CatalogSearch\Model\Advanced::class, - ['addFilters', '__wakeup'] + ['addFilters', '__wakeup', 'getProductCollection'] ); $catalogSearchAdvanced->expects($this->once())->method('addFilters')->will( $this->returnCallback( @@ -38,6 +44,8 @@ function ($added) use (&$filters) { } ) ); + $catalogSearchAdvanced->expects($this->once())->method('getProductCollection') + ->will($this->returnValue($collection)); $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $context = $objectManager->getObject( @@ -138,4 +146,43 @@ public function testUrlSetOnException() ); $this->assertEquals($redirectResultMock, $instance->execute()); } + + public function testNoResultsHandle() + { + $expectedQuery = 'notExistTerm'; + + $view = $this->createPartialMock(\Magento\Framework\App\View::class, ['loadLayout', 'renderLayout']); + $view->expects($this->once())->method('loadLayout') + ->with([\Magento\CatalogSearch\Controller\Advanced\Result::DEFAULT_NO_RESULT_HANDLE]); + + $request = $this->createPartialMock(\Magento\Framework\App\Console\Request::class, ['getQueryValue']); + $request->expects($this->once())->method('getQueryValue')->will($this->returnValue($expectedQuery)); + + $collection = $this->createPartialMock( + \Magento\CatalogSearch\Model\ResourceModel\Advanced\Collection::class, + ['getSize'] + ); + $collection->expects($this->once())->method('getSize')->will($this->returnValue(0)); + + $catalogSearchAdvanced = $this->createPartialMock( + \Magento\CatalogSearch\Model\Advanced::class, + ['addFilters', '__wakeup', 'getProductCollection'] + ); + + $catalogSearchAdvanced->expects($this->once())->method('getProductCollection') + ->will($this->returnValue($collection)); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $context = $objectManager->getObject( + \Magento\Framework\App\Action\Context::class, + ['view' => $view, 'request' => $request] + ); + + /** @var \Magento\CatalogSearch\Controller\Advanced\Result $instance */ + $instance = $objectManager->getObject( + \Magento\CatalogSearch\Controller\Advanced\Result::class, + ['context' => $context, 'catalogSearchAdvanced' => $catalogSearchAdvanced] + ); + $instance->execute(); + } } From 158a0fd7a2b452d05744312bdddde287b292e494 Mon Sep 17 00:00:00 2001 From: Yevhenii Dumskyi Date: Tue, 25 Sep 2018 22:05:57 +0300 Subject: [PATCH 019/704] Remove unused method --- .../CatalogSearch/Controller/Advanced/Result.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php b/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php index 78890643c59fb..dfbd46aa6a1cf 100644 --- a/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php +++ b/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php @@ -79,14 +79,4 @@ public function execute() return $resultRedirect; } } - - /** - * Returns no result handle - * - * @return string - */ - private function getNoResultsHandle() - { - return self::DEFAULT_NO_RESULT_HANDLE; - } } From 406d00003ee1190714ee90b53577a4eaf4c062ad Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun Date: Wed, 26 Sep 2018 17:04:42 +0300 Subject: [PATCH 020/704] Fix code stryling issues #18170 --- app/code/Magento/Customer/Model/AccountManagement.php | 4 ++++ .../Customer/Test/Unit/Model/AccountManagementTest.php | 10 ---------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index a7db70d14fad1..c2f46b24985af 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -970,6 +970,8 @@ protected function createPasswordHash($password) } /** + * Get attribute validator + * * @return Backend */ private function getEavValidator() @@ -1168,6 +1170,8 @@ protected function getWebsiteStoreId($customer, $defaultStoreId = null) } /** + * Get available email template types + * * @return array * @deprecated 100.1.0 */ diff --git a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php index 0190ebcc4457e..27880e44a7239 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php @@ -1079,9 +1079,7 @@ protected function prepareInitiatePasswordReset($email, $templateIdentifier, $se { $websiteId = 1; $addressId = 5; - $datetime = $this->prepareDateTimeFactory(); - $customerData = ['key' => 'value']; $customerName = 'Customer Name'; @@ -1091,7 +1089,6 @@ protected function prepareInitiatePasswordReset($email, $templateIdentifier, $se $this->store->expects($this->any()) ->method('getId') ->willReturn($storeId); - $this->storeManager->expects($this->any()) ->method('getStore') ->willReturn($this->store); @@ -1121,7 +1118,6 @@ protected function prepareInitiatePasswordReset($email, $templateIdentifier, $se $customer->expects($this->any()) ->method('getAddresses') ->willReturn([$address]); - $this->customerRepository->expects($this->once()) ->method('get') ->willReturn($customer); @@ -1132,7 +1128,6 @@ protected function prepareInitiatePasswordReset($email, $templateIdentifier, $se $addressModel->expects($this->once()) ->method('setShouldIgnoreValidation') ->with(true); - $this->customerRepository->expects($this->once()) ->method('get') ->with($email, $websiteId) @@ -1141,16 +1136,13 @@ protected function prepareInitiatePasswordReset($email, $templateIdentifier, $se ->method('save') ->with($customer) ->willReturnSelf(); - $this->random->expects($this->once()) ->method('getUniqueHash') ->willReturn($hash); - $this->customerViewHelper->expects($this->any()) ->method('getCustomerName') ->with($customer) ->willReturn($customerName); - $this->customerSecure->expects($this->any()) ->method('setRpToken') ->with($hash) @@ -1167,12 +1159,10 @@ protected function prepareInitiatePasswordReset($email, $templateIdentifier, $se ->method('setData') ->with('name', $customerName) ->willReturnSelf(); - $this->customerRegistry->expects($this->any()) ->method('retrieveSecureData') ->with($customerId) ->willReturn($this->customerSecure); - $this->dataObjectProcessor->expects($this->any()) ->method('buildOutputDataArray') ->with($customer, \Magento\Customer\Api\Data\CustomerInterface::class) From 09c9e6bed435aada9b2f38800087f60ef134d7c4 Mon Sep 17 00:00:00 2001 From: Pablo Fantini Date: Mon, 24 Sep 2018 11:07:05 -0300 Subject: [PATCH 021/704] GraphQL-57: Manager Address Book --- .../Model/Resolver/Address.php | 374 ++++++++++++++++++ .../Resolver/Address/AddressDataProvider.php | 82 ++++ .../CustomerGraphQl/etc/schema.graphqls | 290 ++++++++++++++ 3 files changed, 746 insertions(+) create mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php create mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php new file mode 100644 index 0000000000000..4c64273cacdaf --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php @@ -0,0 +1,374 @@ +customerRepository = $customerRepository; + $this->addressRepositoryInterface = $addressRepositoryInterface; + $this->addressInterfaceFactory = $addressInterfaceFactory; + $this->regionInterfaceFactory = $regionInterfaceFactory; + $this->attributeInterfaceFactory = $attributeInterfaceFactory; + $this->addressExtensionInterfaceFactory = $addressExtensionInterfaceFactory; + $this->eavConfig = $eavConfig; + $this->addressDataProvider = $addressDataProvider; + } + + /** + * {@inheritdoc} + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + /** @var ContextInterface $context */ + if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { + throw new GraphQlAuthorizationException( + __( + 'Current customer does not have access to the resource "%1"', + [\Magento\Customer\Model\Customer::ENTITY] + ) + ); + } + /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ + $customer = $this->customerRepository->getById($context->getUserId()); + switch ($field->getName()) { + case self::MUTATION_ADDRESS_CREATE: + return $this->addressDataProvider->processCustomerAddress( + $this->processCustomerAddressCreate($customer, $args['input']) + ); + case self::MUTATION_ADDRESS_UPDATE: + return $this->addressDataProvider->processCustomerAddress( + $this->processCustomerAddressUpdate($customer, $args['id'], $args['input']) + ); + case self::MUTATION_ADDRESS_DELETE: + return $this->processCustomerAddressDelete($customer, $args['id']); + default: + return []; + } + } + + /** + * Get input address attribute errors + * @param $addressInput + * @return bool|string + */ + private function getAddressInputError($addressInput) + { + foreach (self::ADDRESS_ATTRIBUTES as $attribute) { + if ($this->isAttributeRequired($attribute) && !isset($addressInput[$attribute])) { + return $attribute; + } + } + return false; + } + + /** + * Check if attribute is set as required + * @param $attributeName + * @return bool + */ + private function isAttributeRequired($attributeName) + { + return $this->eavConfig->getAttribute( + AddressMetadataManagementInterface::ENTITY_TYPE_ADDRESS, + $attributeName + )->getIsRequired(); + } + + /** + * @param AddressInterface $address + * @param array $addressInput + * @return AddressInterface + */ + private function fillAddress($address, $addressInput) + { + if (isset($addressInput[AddressInterface::REGION])) { + /** @var \Magento\Customer\Api\Data\RegionInterface $newRegion */ + $newRegion = $this->regionInterfaceFactory->create($addressInput[AddressInterface::REGION]); + $address->setRegion($newRegion); + } + if (isset($addressInput[AddressInterface::REGION_ID])) { + $address->setRegionId($addressInput[AddressInterface::REGION_ID]); + } + if (isset($addressInput[AddressInterface::COUNTRY_ID])) { + $address->setCountryId($addressInput[AddressInterface::COUNTRY_ID]); + } + if (isset($addressInput[AddressInterface::STREET])) { + $address->setStreet($addressInput[AddressInterface::STREET]); + } + if (isset($addressInput[AddressInterface::COMPANY])) { + $address->setCompany($addressInput[AddressInterface::COMPANY]); + } + if (isset($addressInput[AddressInterface::TELEPHONE])) { + $address->setTelephone($addressInput[AddressInterface::TELEPHONE]); + } + if (isset($addressInput[AddressInterface::FAX])) { + $address->setFax($addressInput[AddressInterface::FAX]); + } + if (isset($addressInput[AddressInterface::POSTCODE])) { + $address->setPostcode($addressInput[AddressInterface::POSTCODE]); + } + if (isset($addressInput[AddressInterface::CITY])) { + $address->setCity($addressInput[AddressInterface::CITY]); + } + if (isset($addressInput[AddressInterface::FIRSTNAME])) { + $address->setFirstname($addressInput[AddressInterface::FIRSTNAME]); + } + if (isset($addressInput[AddressInterface::LASTNAME])) { + $address->setLastname($addressInput[AddressInterface::LASTNAME]); + } + if (isset($addressInput[AddressInterface::MIDDLENAME])) { + $address->setMiddlename($addressInput[AddressInterface::MIDDLENAME]); + } + if (isset($addressInput[AddressInterface::PREFIX])) { + $address->setPrefix($addressInput[AddressInterface::PREFIX]); + } + if (isset($addressInput[AddressInterface::SUFFIX])) { + $address->setSuffix($addressInput[AddressInterface::SUFFIX]); + } + if (isset($addressInput[AddressInterface::VAT_ID])) { + $address->setVatId($addressInput[AddressInterface::VAT_ID]); + } + if (isset($addressInput[AddressInterface::DEFAULT_BILLING])) { + $address->setIsDefaultBilling($addressInput[AddressInterface::DEFAULT_BILLING]); + } + if (isset($addressInput[AddressInterface::DEFAULT_SHIPPING])) { + $address->setIsDefaultShipping($addressInput[AddressInterface::DEFAULT_SHIPPING]); + } + if (isset($addressInput[AddressInterface::DEFAULT_SHIPPING])) { + $address->setIsDefaultShipping($addressInput[AddressInterface::DEFAULT_SHIPPING]); + } + if (isset($addressInput[self::CUSTOM_ATTRIBUTE_KEY])) { + foreach ($addressInput[self::CUSTOM_ATTRIBUTE_KEY] as $attribute) { + $address->setCustomAttribute($attribute['attribute_code'], $attribute['value']); + } + } + if (isset($addressInput[self::EXTENSION_ATTRIBUTE_KEY])) { + $extensionAttributes = $address->getExtensionAttributes(); + if (!$extensionAttributes) { + /** @var \Magento\Customer\Api\Data\AddressExtensionInterface $newExtensionAttribute */ + $extensionAttributes = $this->addressExtensionInterfaceFactory->create(); + } + foreach ($addressInput[self::EXTENSION_ATTRIBUTE_KEY] as $attribute) { + $extensionAttributes->setData($attribute['attribute_code'], $attribute['value']); + } + $address->setExtensionAttributes($extensionAttributes); + } + return $address; + } + + /** + * Process customer address create + * @param CustomerInterface $customer + * @param array $addressInput + * @return AddressInterface + * @throws GraphQlInputException + */ + private function processCustomerAddressCreate($customer, $addressInput) + { + $errorInput = $this->getAddressInputError($addressInput); + if ($errorInput) { + throw new GraphQlInputException(__('Required parameter %1 is missing', [$errorInput])); + } + /** @var AddressInterface $newAddress */ + $newAddress = $this->fillAddress( + $this->addressInterfaceFactory->create(), + $addressInput + ); + $newAddress->setCustomerId($customer->getId()); + return $this->addressRepositoryInterface->save($newAddress); + } + + /** + * Process customer address update + * @param CustomerInterface $customer + * @param int $addressId + * @param array $addressInput + * @return AddressInterface + * @throws GraphQlAuthorizationException + * @throws GraphQlNoSuchEntityException + */ + private function processCustomerAddressUpdate($customer, $addressId, $addressInput) + { + try { + /** @var AddressInterface $address */ + $address = $this->addressRepositoryInterface->getById($addressId); + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException( + __('Address id %1 does not exist.', [$addressId]) + ); + } + if ($address->getCustomerId() != $customer->getId()) { + throw new GraphQlAuthorizationException( + __('Current customer does not have permission to update address id %1', [$addressId]) + ); + } + return $this->addressRepositoryInterface->save( + $this->fillAddress($address, $addressInput) + ); + } + + /** + * Process customer address delete + * @param CustomerInterface $customer + * @param int $addressId + * @return bool + * @throws GraphQlAuthorizationException + * @throws GraphQlNoSuchEntityException + */ + private function processCustomerAddressDelete($customer, $addressId) + { + try { + /** @var AddressInterface $address */ + $address = $this->addressRepositoryInterface->getById($addressId); + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException( + __('Address id %1 does not exist.', [$addressId]) + ); + } + if ($address->getCustomerId() != $customer->getId()) { + throw new GraphQlAuthorizationException( + __('Current customer does not have permission to delete address id %1', [$addressId]) + ); + } + if ($address->isDefaultBilling()) { + throw new GraphQlAuthorizationException( + __('Customer Address %1 is set as default billing address and can not be deleted', [$addressId]) + ); + } + if ($address->isDefaultShipping()) { + throw new GraphQlAuthorizationException( + __('Customer Address %1 is set as default shipping address and can not be deleted', [$addressId]) + ); + } + return $this->addressRepositoryInterface->delete($address); + } +} \ No newline at end of file diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php new file mode 100644 index 0000000000000..042aeaf031de0 --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php @@ -0,0 +1,82 @@ +serviceOutputProcessor = $serviceOutputProcessor; + $this->jsonSerializer = $jsonSerializer; + } + + /** + * Transform single customer address data from object to in array format + * + * @param AddressInterface $addressObject + * @return array + */ + public function processCustomerAddress(AddressInterface $addressObject) : array + { + $address = $this->serviceOutputProcessor->process( + $addressObject, + AddressRepositoryInterface::class, + 'getById' + ); + if (isset($address['extension_attributes'])) { + $address = array_merge($address, $address['extension_attributes']); + } + $customAttributes = []; + if (isset($address['custom_attributes'])) { + foreach ($address['custom_attributes'] as $attribute) { + $isArray = false; + if (is_array($attribute['value'])) { + $isArray = true; + foreach ($attribute['value'] as $attributeValue) { + if (is_array($attributeValue)) { + $customAttributes[$attribute['attribute_code']] = $this->jsonSerializer->serialize( + $attribute['value'] + ); + continue; + } + $customAttributes[$attribute['attribute_code']] = implode(',', $attribute['value']); + continue; + } + } + if ($isArray) { + continue; + } + $customAttributes[$attribute['attribute_code']] = $attribute['value']; + } + } + $address = array_merge($address, $customAttributes); + + return $address; + } +} \ No newline at end of file diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index 93c741adea87b..c53bfa645d807 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -8,6 +8,42 @@ type Query { type Mutation { generateCustomerToken(email: String!, password: String!): CustomerToken @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\Customer\\Account\\GenerateCustomerToken") @doc(description:"Retrieve Customer token") changeCustomerPassword(currentPassword: String!, newPassword: String!): Customer @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\Customer\\Account\\ChangePassword") @doc(description:"Changes password for logged in customer") + customerAddressCreate(input: CustomerAddressInput!): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\Address") @doc(description: "Create customer address") + customerAddressUpdate(id: Int!, input: CustomerAddressInput): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\Address") @doc(description: "Update customer address") + customerAddressDelete(id: Int!): Boolean @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\Address") @doc(description: "Delete customer address") +} + +input CustomerAddressInput { + region: CustomerAddressRegionInput @doc(description: "An object containing the region name, region code, and region ID") + region_id: Int @doc(description: "A number that uniquely identifies the state, province, or other area") + country_id: CountryCodeEnum @doc(description: "The customer's country") + street: [String] @doc(description: "An array of strings that define the street number and name") + company: String @doc(description: "The customer's company") + telephone: String @doc(description: "The telephone number") + fax: String @doc(description: "The fax number") + postcode: String @doc(description: "The customer's ZIP or postal code") + city: String @doc(description: "The city or town") + firstname: String @doc(description: "The first name of the person associated with the shipping/billing address") + lastname: String @doc(description: "The family name of the person associated with the shipping/billing address") + middlename: String @doc(description: "The middle name of the person associated with the shipping/billing address") + prefix: String @doc(description: "An honorific, such as Dr., Mr., or Mrs.") + suffix: String @doc(description: "A value such as Sr., Jr., or III") + vat_id: String @doc(description: "The customer's Tax/VAT number (for corporate customers)") + default_shipping: Boolean @doc(description: "Indicates whether the address is the default shipping address") + default_billing: Boolean @doc(description: "Indicates whether the address is the default billing address") + custom_attributes: [CustomerAddressAttributeInput] @doc(description: "Address custom attributes") + extension_attributes: [CustomerAddressAttributeInput] @doc(description: "Address extension attributes") +} + +input CustomerAddressRegionInput @doc(description: "CustomerAddressRegionInput defines the customer's state or province") { + region_code: String @doc(description: "The address region code") + region: String @doc(description: "The state or province name") + region_id: Int @doc(description: "Uniquely identifies the region") +} + +input CustomerAddressAttributeInput { + attribute_code: String! @doc(description: "Attribute code") + value: String! @doc(description: "Attribute value") } type CustomerToken { @@ -52,6 +88,8 @@ type CustomerAddress @doc(description: "CustomerAddress contains detailed inform vat_id: String @doc(description: "The customer's Tax/VAT number (for corporate customers)") default_shipping: Boolean @doc(description: "Indicates whether the address is the default shipping address") default_billing: Boolean @doc(description: "Indicates whether the address is the default billing address") + custom_attributes: [CustomerAddressAttribute] @doc(description: "Address custom attributes") + extension_attributes: [CustomerAddressAttribute] @doc(description: "Address extension attributes") } type CustomerAddressRegion @doc(description: "CustomerAddressRegion defines the customer's state or province") { @@ -60,3 +98,255 @@ type CustomerAddressRegion @doc(description: "CustomerAddressRegion defines the region_id: Int @doc(description: "Uniquely identifies the region") } +type CustomerAddressAttribute { + attribute_code: String @doc(description: "Attribute code") + value: String @doc(description: "Attribute value") +} + +enum CountryCodeEnum @doc(description: "The list of countries codes") { + AF @doc(description: "Afghanistan") + AX @doc(description: "Åland Islands") + AL @doc(description: "Albania") + DZ @doc(description: "Algeria") + AS @doc(description: "American Samoa") + AD @doc(description: "Andorra") + AO @doc(description: "Angola") + AI @doc(description: "Anguilla") + AQ @doc(description: "Antarctica") + AG @doc(description: "Antigua & Barbuda") + AR @doc(description: "Argentina") + AM @doc(description: "Armenia") + AW @doc(description: "Aruba") + AU @doc(description: "Australia") + AT @doc(description: "Austria") + AZ @doc(description: "Azerbaijan") + BS @doc(description: "Bahamas") + BH @doc(description: "Bahrain") + BD @doc(description: "Bangladesh") + BB @doc(description: "Barbados") + BY @doc(description: "Belarus") + BE @doc(description: "Belgium") + BZ @doc(description: "Belize") + BJ @doc(description: "Benin") + BM @doc(description: "Bermuda") + BT @doc(description: "Bhutan") + BO @doc(description: "Bolivia") + BA @doc(description: "Bosnia & Herzegovina") + BW @doc(description: "Botswana") + BV @doc(description: "Bouvet Island") + BR @doc(description: "Brazil") + IO @doc(description: "British Indian Ocean Territory") + VG @doc(description: "British Virgin Islands") + BN @doc(description: "Brunei") + BG @doc(description: "Bulgaria") + BF @doc(description: "Burkina Faso") + BI @doc(description: "Burundi") + KH @doc(description: "Cambodia") + CM @doc(description: "Cameroon") + CA @doc(description: "Canada") + CV @doc(description: "Cape Verde") + KY @doc(description: "Cayman Islands") + CF @doc(description: "Central African Republic") + TD @doc(description: "Chad") + CL @doc(description: "Chile") + CN @doc(description: "China") + CX @doc(description: "Christmas Island") + CC @doc(description: "Cocos (Keeling) Islands") + CO @doc(description: "Colombia") + KM @doc(description: "Comoros") + CG @doc(description: "Congo -Brazzaville") + CD @doc(description: "Congo - Kinshasa") + CK @doc(description: "Cook Islands") + CR @doc(description: "Costa Rica") + CI @doc(description: "Côte d’Ivoire") + HR @doc(description: "Croatia") + CU @doc(description: "Cuba") + CY @doc(description: "Cyprus") + CZ @doc(description: "Czech Republic") + DK @doc(description: "Denmark") + DJ @doc(description: "Djibouti") + DM @doc(description: "Dominica") + DO @doc(description: "Dominican Republic") + EC @doc(description: "Ecuador") + EG @doc(description: "Egypt") + SV @doc(description: "El Salvador") + GQ @doc(description: "Equatorial Guinea") + ER @doc(description: "Eritrea") + EE @doc(description: "Estonia") + ET @doc(description: "Ethiopia") + FK @doc(description: "Falkland Islands") + FO @doc(description: "Faroe Islands") + FJ @doc(description: "Fiji") + FI @doc(description: "Finland") + FR @doc(description: "France") + GF @doc(description: "French Guiana") + PF @doc(description: "French Polynesia") + TF @doc(description: "French Southern Territories") + GA @doc(description: "Gabon") + GM @doc(description: "Gambia") + GE @doc(description: "Georgia") + DE @doc(description: "Germany") + GH @doc(description: "Ghana") + GI @doc(description: "Gibraltar") + GR @doc(description: "Greece") + GL @doc(description: "Greenland") + GD @doc(description: "Grenada") + GP @doc(description: "Guadeloupe") + GU @doc(description: "Guam") + GT @doc(description: "Guatemala") + GG @doc(description: "Guernsey") + GN @doc(description: "Guinea") + GW @doc(description: "Guinea-Bissau") + GY @doc(description: "Guyana") + HT @doc(description: "Haiti") + HM @doc(description: "Heard & McDonald Islands") + HN @doc(description: "Honduras") + HK @doc(description: "Hong Kong SAR China") + HU @doc(description: "Hungary") + IS @doc(description: "Iceland") + IN @doc(description: "India") + ID @doc(description: "Indonesia") + IR @doc(description: "Iran") + IQ @doc(description: "Iraq") + IE @doc(description: "Ireland") + IM @doc(description: "Isle of Man") + IL @doc(description: "Israel") + IT @doc(description: "Italy") + JM @doc(description: "Jamaica") + JP @doc(description: "Japan") + JE @doc(description: "Jersey") + JO @doc(description: "Jordan") + KZ @doc(description: "Kazakhstan") + KE @doc(description: "Kenya") + KI @doc(description: "Kiribati") + KW @doc(description: "Kuwait") + KG @doc(description: "Kyrgyzstan") + LA @doc(description: "Laos") + LV @doc(description: "Latvia") + LB @doc(description: "Lebanon") + LS @doc(description: "Lesotho") + LR @doc(description: "Liberia") + LY @doc(description: "Libya") + LI @doc(description: "Liechtenstein") + LT @doc(description: "Lithuania") + LU @doc(description: "Luxembourg") + MO @doc(description: "Macau SAR China") + MK @doc(description: "Macedonia") + MG @doc(description: "Madagascar") + MW @doc(description: "Malawi") + MY @doc(description: "Malaysia") + MV @doc(description: "Maldives") + ML @doc(description: "Mali") + MT @doc(description: "Malta") + MH @doc(description: "Marshall Islands") + MQ @doc(description: "Martinique") + MR @doc(description: "Mauritania") + MU @doc(description: "Mauritius") + YT @doc(description: "Mayotte") + MX @doc(description: "Mexico") + FM @doc(description: "Micronesia") + MD @doc(description: "Moldova") + MC @doc(description: "Monaco") + MN @doc(description: "Mongolia") + ME @doc(description: "Montenegro") + MS @doc(description: "Montserrat") + MA @doc(description: "Morocco") + MZ @doc(description: "Mozambique") + MM @doc(description: "Myanmar (Burma)") + NA @doc(description: "Namibia") + NR @doc(description: "Nauru") + NP @doc(description: "Nepal") + NL @doc(description: "Netherlands") + AN @doc(description: "Netherlands Antilles") + NC @doc(description: "New Caledonia") + NZ @doc(description: "New Zealand") + NI @doc(description: "Nicaragua") + NE @doc(description: "Niger") + NG @doc(description: "Nigeria") + NU @doc(description: "Niue") + NF @doc(description: "Norfolk Island") + MP @doc(description: "Northern Mariana Islands") + KP @doc(description: "North Korea") + NO @doc(description: "Norway") + OM @doc(description: "Oman") + PK @doc(description: "Pakistan") + PW @doc(description: "Palau") + PS @doc(description: "Palestinian Territories") + PA @doc(description: "Panama") + PG @doc(description: "Papua New Guinea") + PY @doc(description: "Paraguay") + PE @doc(description: "Peru") + PH @doc(description: "Philippines") + PN @doc(description: "Pitcairn Islands") + PL @doc(description: "Poland") + PT @doc(description: "Portugal") + QA @doc(description: "Qatar") + RE @doc(description: "Réunion") + RO @doc(description: "Romania") + RU @doc(description: "Russia") + RW @doc(description: "Rwanda") + WS @doc(description: "Samoa") + SM @doc(description: "San Marino") + ST @doc(description: "São Tomé & Príncipe") + SA @doc(description: "Saudi Arabia") + SN @doc(description: "Senegal") + RS @doc(description: "Serbia") + SC @doc(description: "Seychelles") + SL @doc(description: "Sierra Leone") + SG @doc(description: "Singapore") + SK @doc(description: "Slovakia") + SI @doc(description: "Slovenia") + SB @doc(description: "Solomon Islands") + SO @doc(description: "Somalia") + ZA @doc(description: "South Africa") + GS @doc(description: "South Georgia & South Sandwich Islands") + KR @doc(description: "South Korea") + ES @doc(description: "Spain") + LK @doc(description: "Sri Lanka") + BL @doc(description: "St. Barthélemy") + SH @doc(description: "St. Helena") + KN @doc(description: "St. Kitts & Nevis") + LC @doc(description: "St. Lucia") + MF @doc(description: "St. Martin") + PM @doc(description: "St. Pierre & Miquelon") + VC @doc(description: "St. Vincent & Grenadines") + SD @doc(description: "Sudan") + SR @doc(description: "Suriname") + SJ @doc(description: "Svalbard & Jan Mayen") + SZ @doc(description: "Swaziland") + SE @doc(description: "Sweden") + CH @doc(description: "Switzerland") + SY @doc(description: "Syria") + TW @doc(description: "Taiwan") + TJ @doc(description: "Tajikistan") + TZ @doc(description: "Tanzania") + TH @doc(description: "Thailand") + TL @doc(description: "Timor-Leste") + TG @doc(description: "Togo") + TK @doc(description: "Tokelau") + TO @doc(description: "Tonga") + TT @doc(description: "Trinidad & Tobago") + TN @doc(description: "Tunisia") + TR @doc(description: "Turkey") + TM @doc(description: "Turkmenistan") + TC @doc(description: "Turks & Caicos Islands") + TV @doc(description: "Tuvalu") + UG @doc(description: "Uganda") + UA @doc(description: "Ukraine") + AE @doc(description: "United Arab Emirates") + GB @doc(description: "United Kingdom") + US @doc(description: "United States") + UY @doc(description: "Uruguay") + UM @doc(description: "U.S. Outlying Islands") + VI @doc(description: "U.S. Virgin Islands") + UZ @doc(description: "Uzbekistan") + VU @doc(description: "Vanuatu") + VA @doc(description: "Vatican City") + VE @doc(description: "Venezuela") + VN @doc(description: "Vietnam") + WF @doc(description: "Wallis & Futuna") + EH @doc(description: "Western Sahara") + YE @doc(description: "Yemen") + ZM @doc(description: "Zambia") + ZW @doc(description: "Zimbabwe") +} \ No newline at end of file From fbefe295047a64ed94c60e7bc2adfb2730916c1b Mon Sep 17 00:00:00 2001 From: Pablo Fantini Date: Wed, 26 Sep 2018 11:11:46 -0300 Subject: [PATCH 022/704] GraphQL-57: Add functional test --- .../Model/Resolver/Address.php | 7 +- .../GraphQl/Customer/CustomerAddressTest.php | 521 ++++++++++++++++++ 2 files changed, 523 insertions(+), 5 deletions(-) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressTest.php diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php index 4c64273cacdaf..c28d0f3c24d23 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php @@ -258,13 +258,10 @@ private function fillAddress($address, $addressInput) $address->setVatId($addressInput[AddressInterface::VAT_ID]); } if (isset($addressInput[AddressInterface::DEFAULT_BILLING])) { - $address->setIsDefaultBilling($addressInput[AddressInterface::DEFAULT_BILLING]); + $address->setIsDefaultBilling((bool)$addressInput[AddressInterface::DEFAULT_BILLING]); } if (isset($addressInput[AddressInterface::DEFAULT_SHIPPING])) { - $address->setIsDefaultShipping($addressInput[AddressInterface::DEFAULT_SHIPPING]); - } - if (isset($addressInput[AddressInterface::DEFAULT_SHIPPING])) { - $address->setIsDefaultShipping($addressInput[AddressInterface::DEFAULT_SHIPPING]); + $address->setIsDefaultShipping((bool)$addressInput[AddressInterface::DEFAULT_SHIPPING]); } if (isset($addressInput[self::CUSTOM_ATTRIBUTE_KEY])) { foreach ($addressInput[self::CUSTOM_ATTRIBUTE_KEY] as $attribute) { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressTest.php new file mode 100644 index 0000000000000..7141c2d15d4b2 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressTest.php @@ -0,0 +1,521 @@ + [ + 'region' => 'Alaska', + 'region_id' => 4, + 'region_code' => 'AK' + ], + 'region_id' => 4, + 'country_id' => 'US', + 'street' => ['Line 1 Street', 'Line 2'], + 'company' => 'Company name', + 'telephone' => '123456789', + 'fax' => '123123123', + 'postcode' => '7777', + 'city' => 'City Name', + 'firstname' => 'Adam', + 'lastname' => 'Phillis', + 'middlename' => 'A', + 'prefix' => 'Mr.', + 'suffix' => 'Jr.', + 'vat_id' => '1', + 'default_shipping' => false, + 'default_billing' => false + ]; + $defaultShippingText = $newAddress['default_shipping'] ? "true": "false"; + $defaultBillingText = $newAddress['default_billing'] ? "true": "false"; + $mutation + = <<get(CustomerTokenServiceInterface::class); + $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get($userName); + $response = $this->graphQlQuery($mutation, [], '', $headerMap); + $this->assertArrayHasKey('customerAddressCreate', $response); + $this->assertArrayHasKey('customer_id', $response['customerAddressCreate']); + $this->assertEquals($customer->getId(), $response['customerAddressCreate']['customer_id']); + $this->assertArrayHasKey('id', $response['customerAddressCreate']); + /** @var AddressRepositoryInterface $addressRepository */ + $addressRepository = ObjectManager::getInstance()->get(AddressRepositoryInterface::class); + $addressId = $response['customerAddressCreate']['id']; + $address = $addressRepository->getById($addressId); + $this->assertEquals($address->getId(), $response['customerAddressCreate']['id']); + $this->assertCustomerAddressesFields($address, $response['customerAddressCreate']); + $this->assertCustomerAddressesFields($address, $newAddress); + } + + /** + * Verify customers without credentials create new address + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testAddCustomerAddressWithoutCredentials() + { + $mutation + = <<expectException(\Exception::class); + $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . + 'Current customer does not have access to the resource "customer"'); + $this->graphQlQuery($mutation); + } + + /** + * Verify customers with valid credentials update address + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_address.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testUpdateCustomerAddressWithValidCredentials() + { + $userName = 'customer@example.com'; + $password = 'password'; + $updateAddress = [ + 'region' => [ + 'region' => 'Alaska', + 'region_id' => 4, + 'region_code' => 'AK' + ], + 'region_id' => 4, + 'country_id' => 'US', + 'street' => ['Line 1 Street', 'Line 2'], + 'company' => 'Company Name', + 'telephone' => '123456789', + 'fax' => '123123123', + 'postcode' => '7777', + 'city' => 'City Name', + 'firstname' => 'Adam', + 'lastname' => 'Phillis', + 'middlename' => 'A', + 'prefix' => 'Mr.', + 'suffix' => 'Jr.', + 'vat_id' => '1', + 'default_shipping' => true, + 'default_billing' => true + ]; + $defaultShippingText = $updateAddress['default_shipping'] ? "true": "false"; + $defaultBillingText = $updateAddress['default_billing'] ? "true": "false"; + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get($userName); + /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ + $addresses = $customer->getAddresses(); + /** @var \Magento\Customer\Api\Data\AddressInterface $address */ + $address = current($addresses); + $addressId = $address->getId(); + $mutation + = <<get(CustomerTokenServiceInterface::class); + $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get($userName); + $response = $this->graphQlQuery($mutation, [], '', $headerMap); + $this->assertArrayHasKey('customerAddressUpdate', $response); + $this->assertArrayHasKey('customer_id', $response['customerAddressUpdate']); + $this->assertEquals($customer->getId(), $response['customerAddressUpdate']['customer_id']); + $this->assertArrayHasKey('id', $response['customerAddressUpdate']); + /** @var AddressRepositoryInterface $addressRepository */ + $addressRepository = ObjectManager::getInstance()->get(AddressRepositoryInterface::class); + $address = $addressRepository->getById($addressId); + $this->assertEquals($address->getId(), $response['customerAddressUpdate']['id']); + $this->assertCustomerAddressesFields($address, $response['customerAddressUpdate']); + $this->assertCustomerAddressesFields($address, $updateAddress); + } + + /** + * Verify customers without credentials update address + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_address.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testUpdateCustomerAddressWithoutCredentials() + { + $userName = 'customer@example.com'; + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get($userName); + /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ + $addresses = $customer->getAddresses(); + /** @var \Magento\Customer\Api\Data\AddressInterface $address */ + $address = current($addresses); + $addressId = $address->getId(); + $mutation + = <<expectException(\Exception::class); + $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . + 'Current customer does not have access to the resource "customer"'); + $this->graphQlQuery($mutation); + } + + /** + * Verify customers with valid credentials with a customer bearer token + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testDeleteCustomerAddressWithValidCredentials() + { + $userName = 'customer@example.com'; + $password = 'password'; + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get($userName); + /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ + $addresses = $customer->getAddresses(); + /** @var \Magento\Customer\Api\Data\AddressInterface $address */ + $address = end($addresses); + $addressId = $address->getId(); + $mutation + = <<get(CustomerTokenServiceInterface::class); + $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + $response = $this->graphQlQuery($mutation, [], '', $headerMap); + $this->assertArrayHasKey('customerAddressDelete', $response); + $this->assertEquals(true, $response['customerAddressDelete']); + } + + /** + * Verify customers without credentials delete address + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_address.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testDeleteCustomerAddressWithoutCredentials() + { + $userName = 'customer@example.com'; + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get($userName); + /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ + $addresses = $customer->getAddresses(); + /** @var \Magento\Customer\Api\Data\AddressInterface $address */ + $address = current($addresses); + $addressId = $address->getId(); + $mutation + = <<expectException(\Exception::class); + $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . + 'Current customer does not have access to the resource "customer"'); + $this->graphQlQuery($mutation); + } + + /** + * Verify customers with valid credentials delete default shipping address + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testDeleteDefaultShippingCustomerAddressWithValidCredentials() + { + $userName = 'customer@example.com'; + $password = 'password'; + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get($userName); + /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ + $addresses = $customer->getAddresses(); + /** @var \Magento\Customer\Api\Data\AddressInterface $address */ + $address = end($addresses); + $address->setIsDefaultShipping(true); + $addressRepository = ObjectManager::getInstance()->get(AddressRepositoryInterface::class); + $addressRepository->save($address); + $addressId = $address->getId(); + $mutation + = <<get(CustomerTokenServiceInterface::class); + $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + $this->expectException(\Exception::class); + $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . + 'Customer Address ' . $addressId . ' is set as default shipping address and can not be deleted'); + $this->graphQlQuery($mutation, [], '', $headerMap); + } + + /** + * Verify customers with valid credentials delete default billing address + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testDeleteDefaultBillingCustomerAddressWithValidCredentials() + { + $userName = 'customer@example.com'; + $password = 'password'; + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get($userName); + /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ + $addresses = $customer->getAddresses(); + /** @var \Magento\Customer\Api\Data\AddressInterface $address */ + $address = end($addresses); + $address->setIsDefaultBilling(true); + $addressRepository = ObjectManager::getInstance()->get(AddressRepositoryInterface::class); + $addressRepository->save($address); + $addressId = $address->getId(); + $mutation + = <<get(CustomerTokenServiceInterface::class); + $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + $this->expectException(\Exception::class); + $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . + 'Customer Address ' . $addressId . ' is set as default billing address and can not be deleted'); + $this->graphQlQuery($mutation, [], '', $headerMap); + } + + /** + * Verify customers with valid credentials delete non exist address + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testDeleteNonExistCustomerAddressWithValidCredentials() + { + $userName = 'customer@example.com'; + $password = 'password'; + $mutation + = <<get(CustomerTokenServiceInterface::class); + $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + $this->expectException(\Exception::class); + $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . + 'Address id 9999 does not exist.'); + $this->graphQlQuery($mutation, [], '', $headerMap); + } + + /** + * Verify the fields for Customer address + * + * @param \Magento\Customer\Api\Data\AddressInterface $address + * @param array $actualResponse + */ + private function assertCustomerAddressesFields($address, $actualResponse) + { + /** @var $addresses */ + $assertionMap = [ + ['response_field' => 'region_id', 'expected_value' => $address->getRegionId()], + ['response_field' => 'country_id', 'expected_value' => $address->getCountryId()], + ['response_field' => 'street', 'expected_value' => $address->getStreet()], + ['response_field' => 'company', 'expected_value' => $address->getCompany()], + ['response_field' => 'telephone', 'expected_value' => $address->getTelephone()], + ['response_field' => 'fax', 'expected_value' => $address->getFax()], + ['response_field' => 'postcode', 'expected_value' => $address->getPostcode()], + ['response_field' => 'city', 'expected_value' => $address->getCity()], + ['response_field' => 'firstname', 'expected_value' => $address->getFirstname()], + ['response_field' => 'lastname', 'expected_value' => $address->getLastname()], + ['response_field' => 'middlename', 'expected_value' => $address->getMiddlename()], + ['response_field' => 'prefix', 'expected_value' => $address->getPrefix()], + ['response_field' => 'suffix', 'expected_value' => $address->getSuffix()], + ['response_field' => 'vat_id', 'expected_value' => $address->getVatId()], + ['response_field' => 'default_shipping', 'expected_value' => (bool)$address->isDefaultShipping()], + ['response_field' => 'default_billing', 'expected_value' => (bool)$address->isDefaultBilling()], + ]; + $this->assertResponseFields($actualResponse, $assertionMap); + $this->assertTrue(is_array([$actualResponse['region']]), "region field must be of an array type."); + $assertionRegionMap = [ + ['response_field' => 'region', 'expected_value' => $address->getRegion()->getRegion()], + ['response_field' => 'region_code', 'expected_value' => $address->getRegion()->getRegionCode()], + ['response_field' => 'region_id', 'expected_value' => $address->getRegion()->getRegionId()] + ]; + $this->assertResponseFields($actualResponse['region'], $assertionRegionMap); + } +} From 7aaba83bf3239076f595407fe07bf41645f7962b Mon Sep 17 00:00:00 2001 From: Pablo Fantini Date: Wed, 26 Sep 2018 16:17:11 -0300 Subject: [PATCH 023/704] Refactor fillAddress and add missing doc --- .../Model/Resolver/Address.php | 150 ++++-------------- .../Resolver/Address/AddressDataProvider.php | 5 +- .../GraphQl/Customer/CustomerAddressTest.php | 2 +- 3 files changed, 39 insertions(+), 118 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php index c28d0f3c24d23..32afe0d65171e 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php @@ -3,24 +3,21 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types = 1); +declare(strict_types=1); namespace Magento\CustomerGraphQl\Model\Resolver; use Magento\Authorization\Model\UserContextInterface; -use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Api\AddressMetadataManagementInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\Data\AddressInterfaceFactory; use Magento\Customer\Api\Data\AddressInterface; -use Magento\Customer\Api\Data\RegionInterfaceFactory; -use Magento\Customer\Api\Data\AddressExtensionInterfaceFactory; -use Magento\Framework\Api\AttributeInterfaceFactory; +use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; -use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; @@ -29,7 +26,7 @@ use Magento\Eav\Model\Config; /** - * Customers Address Update + * Customers Address, used for GraphQL request processing. */ class Address implements ResolverInterface { @@ -68,10 +65,11 @@ class Address implements ResolverInterface const MUTATION_ADDRESS_UPDATE = 'customerAddressUpdate'; const MUTATION_ADDRESS_DELETE = 'customerAddressDelete'; + /** * @var CustomerRepositoryInterface */ - private $customerRepository; + private $customerRepositoryInterface; /** * @var AddressRepositoryInterface @@ -83,21 +81,6 @@ class Address implements ResolverInterface */ private $addressInterfaceFactory; - /** - * @var RegionInterfaceFactory - */ - private $regionInterfaceFactory; - - /** - * @var AttributeInterfaceFactory - */ - private $attributeInterfaceFactory; - - /** - * @var AddressExtensionInterfaceFactory - */ - private $addressExtensionInterfaceFactory; - /** * @var Config */ @@ -109,37 +92,35 @@ class Address implements ResolverInterface private $addressDataProvider; /** - * @param CustomerRepositoryInterface $customerRepository + * @var \Magento\Framework\Api\DataObjectHelper + */ + private $dataObjectHelper; + + /** * @param AddressRepositoryInterface $addressRepositoryInterface * @param AddressInterfaceFactory $addressInterfaceFactory - * @param RegionInterfaceFactory $regionInterfaceFactory - * @param AttributeInterfaceFactory $attributeInterfaceFactory - * @param AddressExtensionInterfaceFactory $addressExtensionInterfaceFactory * @param Config $eavConfig * @param AddressDataProvider $addressDataProvider + * @param DataObjectHelper $dataObjectHelper */ public function __construct( - CustomerRepositoryInterface $customerRepository, + CustomerRepositoryInterface $customerRepositoryInterface, AddressRepositoryInterface $addressRepositoryInterface, AddressInterfaceFactory $addressInterfaceFactory, - RegionInterfaceFactory $regionInterfaceFactory, - AttributeInterfaceFactory $attributeInterfaceFactory, - AddressExtensionInterfaceFactory $addressExtensionInterfaceFactory, Config $eavConfig, - AddressDataProvider $addressDataProvider + AddressDataProvider $addressDataProvider, + DataObjectHelper $dataObjectHelper ) { - $this->customerRepository = $customerRepository; + $this->customerRepositoryInterface = $customerRepositoryInterface; $this->addressRepositoryInterface = $addressRepositoryInterface; $this->addressInterfaceFactory = $addressInterfaceFactory; - $this->regionInterfaceFactory = $regionInterfaceFactory; - $this->attributeInterfaceFactory = $attributeInterfaceFactory; - $this->addressExtensionInterfaceFactory = $addressExtensionInterfaceFactory; $this->eavConfig = $eavConfig; $this->addressDataProvider = $addressDataProvider; + $this->dataObjectHelper = $dataObjectHelper; } /** - * {@inheritdoc} + * @inheritdoc */ public function resolve( Field $field, @@ -148,7 +129,7 @@ public function resolve( array $value = null, array $args = null ) { - /** @var ContextInterface $context */ + /** @var \Magento\Framework\GraphQl\Query\Resolver\ContextInterface $context */ if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { throw new GraphQlAuthorizationException( __( @@ -158,7 +139,7 @@ public function resolve( ); } /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ - $customer = $this->customerRepository->getById($context->getUserId()); + $customer = $this->customerRepositoryInterface->getById($context->getUserId()); switch ($field->getName()) { case self::MUTATION_ADDRESS_CREATE: return $this->addressDataProvider->processCustomerAddress( @@ -177,10 +158,10 @@ public function resolve( /** * Get input address attribute errors - * @param $addressInput + * @param array $addressInput * @return bool|string */ - private function getAddressInputError($addressInput) + private function getAddressInputError(array $addressInput) { foreach (self::ADDRESS_ATTRIBUTES as $attribute) { if ($this->isAttributeRequired($attribute) && !isset($addressInput[$attribute])) { @@ -192,7 +173,7 @@ private function getAddressInputError($addressInput) /** * Check if attribute is set as required - * @param $attributeName + * @param string $attributeName * @return bool */ private function isAttributeRequired($attributeName) @@ -204,81 +185,18 @@ private function isAttributeRequired($attributeName) } /** + * Add $addressInput array information to a $address object * @param AddressInterface $address * @param array $addressInput * @return AddressInterface */ - private function fillAddress($address, $addressInput) + private function fillAddress(AddressInterface $address, array $addressInput) : AddressInterface { - if (isset($addressInput[AddressInterface::REGION])) { - /** @var \Magento\Customer\Api\Data\RegionInterface $newRegion */ - $newRegion = $this->regionInterfaceFactory->create($addressInput[AddressInterface::REGION]); - $address->setRegion($newRegion); - } - if (isset($addressInput[AddressInterface::REGION_ID])) { - $address->setRegionId($addressInput[AddressInterface::REGION_ID]); - } - if (isset($addressInput[AddressInterface::COUNTRY_ID])) { - $address->setCountryId($addressInput[AddressInterface::COUNTRY_ID]); - } - if (isset($addressInput[AddressInterface::STREET])) { - $address->setStreet($addressInput[AddressInterface::STREET]); - } - if (isset($addressInput[AddressInterface::COMPANY])) { - $address->setCompany($addressInput[AddressInterface::COMPANY]); - } - if (isset($addressInput[AddressInterface::TELEPHONE])) { - $address->setTelephone($addressInput[AddressInterface::TELEPHONE]); - } - if (isset($addressInput[AddressInterface::FAX])) { - $address->setFax($addressInput[AddressInterface::FAX]); - } - if (isset($addressInput[AddressInterface::POSTCODE])) { - $address->setPostcode($addressInput[AddressInterface::POSTCODE]); - } - if (isset($addressInput[AddressInterface::CITY])) { - $address->setCity($addressInput[AddressInterface::CITY]); - } - if (isset($addressInput[AddressInterface::FIRSTNAME])) { - $address->setFirstname($addressInput[AddressInterface::FIRSTNAME]); - } - if (isset($addressInput[AddressInterface::LASTNAME])) { - $address->setLastname($addressInput[AddressInterface::LASTNAME]); - } - if (isset($addressInput[AddressInterface::MIDDLENAME])) { - $address->setMiddlename($addressInput[AddressInterface::MIDDLENAME]); - } - if (isset($addressInput[AddressInterface::PREFIX])) { - $address->setPrefix($addressInput[AddressInterface::PREFIX]); - } - if (isset($addressInput[AddressInterface::SUFFIX])) { - $address->setSuffix($addressInput[AddressInterface::SUFFIX]); - } - if (isset($addressInput[AddressInterface::VAT_ID])) { - $address->setVatId($addressInput[AddressInterface::VAT_ID]); - } - if (isset($addressInput[AddressInterface::DEFAULT_BILLING])) { - $address->setIsDefaultBilling((bool)$addressInput[AddressInterface::DEFAULT_BILLING]); - } - if (isset($addressInput[AddressInterface::DEFAULT_SHIPPING])) { - $address->setIsDefaultShipping((bool)$addressInput[AddressInterface::DEFAULT_SHIPPING]); - } - if (isset($addressInput[self::CUSTOM_ATTRIBUTE_KEY])) { - foreach ($addressInput[self::CUSTOM_ATTRIBUTE_KEY] as $attribute) { - $address->setCustomAttribute($attribute['attribute_code'], $attribute['value']); - } - } - if (isset($addressInput[self::EXTENSION_ATTRIBUTE_KEY])) { - $extensionAttributes = $address->getExtensionAttributes(); - if (!$extensionAttributes) { - /** @var \Magento\Customer\Api\Data\AddressExtensionInterface $newExtensionAttribute */ - $extensionAttributes = $this->addressExtensionInterfaceFactory->create(); - } - foreach ($addressInput[self::EXTENSION_ATTRIBUTE_KEY] as $attribute) { - $extensionAttributes->setData($attribute['attribute_code'], $attribute['value']); - } - $address->setExtensionAttributes($extensionAttributes); - } + $this->dataObjectHelper->populateWithArray( + $address, + $addressInput, + \Magento\Customer\Api\Data\AddressInterface::class + ); return $address; } @@ -289,7 +207,7 @@ private function fillAddress($address, $addressInput) * @return AddressInterface * @throws GraphQlInputException */ - private function processCustomerAddressCreate($customer, $addressInput) + private function processCustomerAddressCreate(CustomerInterface $customer, array $addressInput) : AddressInterface { $errorInput = $this->getAddressInputError($addressInput); if ($errorInput) { @@ -313,7 +231,7 @@ private function processCustomerAddressCreate($customer, $addressInput) * @throws GraphQlAuthorizationException * @throws GraphQlNoSuchEntityException */ - private function processCustomerAddressUpdate($customer, $addressId, $addressInput) + private function processCustomerAddressUpdate(CustomerInterface $customer, $addressId, array $addressInput) { try { /** @var AddressInterface $address */ @@ -341,7 +259,7 @@ private function processCustomerAddressUpdate($customer, $addressId, $addressInp * @throws GraphQlAuthorizationException * @throws GraphQlNoSuchEntityException */ - private function processCustomerAddressDelete($customer, $addressId) + private function processCustomerAddressDelete(CustomerInterface $customer, $addressId) { try { /** @var AddressInterface $address */ @@ -368,4 +286,4 @@ private function processCustomerAddressDelete($customer, $addressId) } return $this->addressRepositoryInterface->delete($address); } -} \ No newline at end of file +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php index 042aeaf031de0..b794c4ef58794 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php @@ -12,6 +12,9 @@ use Magento\Framework\Webapi\ServiceOutputProcessor; use Magento\Framework\Serialize\SerializerInterface; +/** + * Customer Address field data provider, used for GraphQL request processing. + */ class AddressDataProvider { /** @@ -79,4 +82,4 @@ public function processCustomerAddress(AddressInterface $addressObject) : array return $address; } -} \ No newline at end of file +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressTest.php index 7141c2d15d4b2..6a1ea8934bfa9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressTest.php @@ -3,7 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -declare(strict_types = 1); +declare(strict_types=1); namespace Magento\GraphQl\Customer; From ebd39d36bb3422eec41a939f5c20a9b1ae2efde3 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun Date: Thu, 27 Sep 2018 08:41:47 +0300 Subject: [PATCH 024/704] Fix the minor code styling issue --- .../Magento/Customer/Model/AccountManagement.php | 10 +++++----- .../Test/Unit/Model/AccountManagementTest.php | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index c2f46b24985af..99aa494e9981f 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -259,7 +259,7 @@ class AccountManagement implements AccountManagementInterface /** * @var CollectionFactory */ - private $visitorCollectionFactory; + private $visitorColFactory; /** * @var DataObjectProcessor @@ -360,7 +360,7 @@ class AccountManagement implements AccountManagementInterface * @param AccountConfirmation|null $accountConfirmation * @param SessionManagerInterface|null $sessionManager * @param SaveHandlerInterface|null $saveHandler - * @param CollectionFactory|null $visitorCollectionFactory + * @param CollectionFactory|null $visitorColFactory * @param AddressRegistry|null $addressRegistry * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -393,7 +393,7 @@ public function __construct( AccountConfirmation $accountConfirmation = null, SessionManagerInterface $sessionManager = null, SaveHandlerInterface $saveHandler = null, - CollectionFactory $visitorCollectionFactory = null, + CollectionFactory $visitorColFactory = null, AddressRegistry $addressRegistry = null ) { $this->customerFactory = $customerFactory; @@ -428,7 +428,7 @@ public function __construct( ?: ObjectManager::getInstance()->get(SessionManagerInterface::class); $this->saveHandler = $saveHandler ?: ObjectManager::getInstance()->get(SaveHandlerInterface::class); - $this->visitorCollectionFactory = $visitorCollectionFactory + $this->visitorColFactory = $visitorColFactory ?: ObjectManager::getInstance()->get(CollectionFactory::class); $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class); @@ -1480,7 +1480,7 @@ private function destroyCustomerSessions($customerId) $activeSessionsTime = $dateTime->setTimestamp($dateTime->getTimestamp() - $sessionLifetime) ->format(DateTime::DATETIME_PHP_FORMAT); /** @var \Magento\Customer\Model\ResourceModel\Visitor\Collection $visitorCollection */ - $visitorCollection = $this->visitorCollectionFactory->create(); + $visitorCollection = $this->visitorColFactory->create(); $visitorCollection->addFieldToFilter('customer_id', $customerId); $visitorCollection->addFieldToFilter('last_visit_at', ['from' => $activeSessionsTime]); $visitorCollection->addFieldToFilter('session_id', ['neq' => $this->sessionManager->getSessionId()]); diff --git a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php index 27880e44a7239..b1e1967aaa630 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php @@ -135,7 +135,7 @@ class AccountManagementTest extends \PHPUnit\Framework\TestCase /** * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Customer\Model\ResourceModel\Visitor\CollectionFactory */ - private $visitorCollectionFactory; + private $visitorColFactory; /** * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Session\SaveHandlerInterface @@ -200,7 +200,7 @@ protected function setUp() $this->dateTimeFactory = $this->createMock(DateTimeFactory::class); $this->accountConfirmation = $this->createMock(AccountConfirmation::class); - $this->visitorCollectionFactory = $this->getMockBuilder( + $this->visitorColFactory = $this->getMockBuilder( \Magento\Customer\Model\ResourceModel\Visitor\CollectionFactory::class ) ->disableOriginalConstructor() @@ -244,7 +244,7 @@ protected function setUp() 'accountConfirmation' => $this->accountConfirmation, 'sessionManager' => $this->sessionManager, 'saveHandler' => $this->saveHandler, - 'visitorCollectionFactory' => $this->visitorCollectionFactory, + 'visitorColFactory' => $this->visitorColFactory, 'addressRegistry' => $this->addressRegistryMock, ] ); @@ -1385,7 +1385,7 @@ private function reInitModel() $this->sessionManager = $this->getMockBuilder(\Magento\Framework\Session\SessionManagerInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->visitorCollectionFactory = $this->getMockBuilder( + $this->visitorColFactory = $this->getMockBuilder( \Magento\Customer\Model\ResourceModel\Visitor\CollectionFactory::class ) ->disableOriginalConstructor() @@ -1431,7 +1431,7 @@ private function reInitModel() 'stringHelper' => $this->string, 'scopeConfig' => $this->scopeConfig, 'sessionManager' => $this->sessionManager, - 'visitorCollectionFactory' => $this->visitorCollectionFactory, + 'visitorColFactory' => $this->visitorColFactory, 'saveHandler' => $this->saveHandler, 'encryptor' => $this->encryptor, 'dataProcessor' => $this->dataObjectProcessor, @@ -1530,7 +1530,7 @@ public function testChangePassword() ->disableOriginalConstructor()->setMethods(['addFieldToFilter', 'getItems'])->getMock(); $visitorCollection->expects($this->atLeastOnce())->method('addFieldToFilter')->willReturnSelf(); $visitorCollection->expects($this->atLeastOnce())->method('getItems')->willReturn([$visitor, $visitor]); - $this->visitorCollectionFactory->expects($this->atLeastOnce())->method('create') + $this->visitorColFactory->expects($this->atLeastOnce())->method('create') ->willReturn($visitorCollection); $this->saveHandler->expects($this->atLeastOnce())->method('destroy') ->withConsecutive( @@ -1584,7 +1584,7 @@ function ($string) { ->disableOriginalConstructor()->setMethods(['addFieldToFilter', 'getItems'])->getMock(); $visitorCollection->expects($this->atLeastOnce())->method('addFieldToFilter')->willReturnSelf(); $visitorCollection->expects($this->atLeastOnce())->method('getItems')->willReturn([$visitor, $visitor]); - $this->visitorCollectionFactory->expects($this->atLeastOnce())->method('create') + $this->visitorColFactory->expects($this->atLeastOnce())->method('create') ->willReturn($visitorCollection); $this->saveHandler->expects($this->atLeastOnce())->method('destroy') ->withConsecutive( From 47adffd0bac94c42659dd74d867640de0f243882 Mon Sep 17 00:00:00 2001 From: Pablo Fantini Date: Thu, 27 Sep 2018 13:18:27 -0300 Subject: [PATCH 025/704] GraphQL-57: Refactor address resolve to check address attributes required dinamically --- .../Model/Resolver/Address.php | 90 +++++++------------ 1 file changed, 30 insertions(+), 60 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php index 32afe0d65171e..185830475ad3c 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php @@ -9,7 +9,6 @@ use Magento\Authorization\Model\UserContextInterface; use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Customer\Api\AddressMetadataManagementInterface; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\Data\AddressInterfaceFactory; @@ -17,11 +16,7 @@ use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; -use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Query\ResolverInterface; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\CustomerGraphQl\Model\Resolver\Address\AddressDataProvider; use Magento\Eav\Model\Config; @@ -30,28 +25,6 @@ */ class Address implements ResolverInterface { - /** - * Customer address attributes - * @var array - */ - const ADDRESS_ATTRIBUTES = [ - AddressInterface::REGION, - AddressInterface::REGION_ID, - AddressInterface::COUNTRY_ID, - AddressInterface::STREET, - AddressInterface::COMPANY, - AddressInterface::TELEPHONE, - AddressInterface::FAX, - AddressInterface::POSTCODE, - AddressInterface::CITY, - AddressInterface::FIRSTNAME, - AddressInterface::LASTNAME, - AddressInterface::MIDDLENAME, - AddressInterface::PREFIX, - AddressInterface::SUFFIX, - AddressInterface::VAT_ID - ]; - /** * Input data key */ @@ -65,7 +38,6 @@ class Address implements ResolverInterface const MUTATION_ADDRESS_UPDATE = 'customerAddressUpdate'; const MUTATION_ADDRESS_DELETE = 'customerAddressDelete'; - /** * @var CustomerRepositoryInterface */ @@ -97,6 +69,12 @@ class Address implements ResolverInterface private $dataObjectHelper; /** + * @var array + */ + private $addressAttributes; + + /** + * @param CustomerRepositoryInterface $customerRepositoryInterface * @param AddressRepositoryInterface $addressRepositoryInterface * @param AddressInterfaceFactory $addressInterfaceFactory * @param Config $eavConfig @@ -117,6 +95,9 @@ public function __construct( $this->eavConfig = $eavConfig; $this->addressDataProvider = $addressDataProvider; $this->dataObjectHelper = $dataObjectHelper; + $this->addressAttributes = $this->eavConfig->getEntityAttributes( + \Magento\Customer\Api\AddressMetadataManagementInterface::ENTITY_TYPE_ADDRESS + ); } /** @@ -131,7 +112,7 @@ public function resolve( ) { /** @var \Magento\Framework\GraphQl\Query\Resolver\ContextInterface $context */ if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { - throw new GraphQlAuthorizationException( + throw new \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException( __( 'Current customer does not have access to the resource "%1"', [\Magento\Customer\Model\Customer::ENTITY] @@ -163,27 +144,14 @@ public function resolve( */ private function getAddressInputError(array $addressInput) { - foreach (self::ADDRESS_ATTRIBUTES as $attribute) { - if ($this->isAttributeRequired($attribute) && !isset($addressInput[$attribute])) { - return $attribute; + foreach ($this->addressAttributes as $attributeName => $attributeInfo) { + if ($attributeInfo->getIsRequired() && !isset($addressInput[$attributeName])) { + return $attributeName; } } return false; } - /** - * Check if attribute is set as required - * @param string $attributeName - * @return bool - */ - private function isAttributeRequired($attributeName) - { - return $this->eavConfig->getAttribute( - AddressMetadataManagementInterface::ENTITY_TYPE_ADDRESS, - $attributeName - )->getIsRequired(); - } - /** * Add $addressInput array information to a $address object * @param AddressInterface $address @@ -195,7 +163,7 @@ private function fillAddress(AddressInterface $address, array $addressInput) : A $this->dataObjectHelper->populateWithArray( $address, $addressInput, - \Magento\Customer\Api\Data\AddressInterface::class + AddressInterface::class ); return $address; } @@ -205,13 +173,15 @@ private function fillAddress(AddressInterface $address, array $addressInput) : A * @param CustomerInterface $customer * @param array $addressInput * @return AddressInterface - * @throws GraphQlInputException + * @throws \Magento\Framework\GraphQl\Exception\GraphQlInputException */ private function processCustomerAddressCreate(CustomerInterface $customer, array $addressInput) : AddressInterface { $errorInput = $this->getAddressInputError($addressInput); if ($errorInput) { - throw new GraphQlInputException(__('Required parameter %1 is missing', [$errorInput])); + throw new \Magento\Framework\GraphQl\Exception\GraphQlInputException( + __('Required parameter %1 is missing', [$errorInput]) + ); } /** @var AddressInterface $newAddress */ $newAddress = $this->fillAddress( @@ -228,21 +198,21 @@ private function processCustomerAddressCreate(CustomerInterface $customer, array * @param int $addressId * @param array $addressInput * @return AddressInterface - * @throws GraphQlAuthorizationException - * @throws GraphQlNoSuchEntityException + * @throws \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException + * @throws \Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException */ private function processCustomerAddressUpdate(CustomerInterface $customer, $addressId, array $addressInput) { try { /** @var AddressInterface $address */ $address = $this->addressRepositoryInterface->getById($addressId); - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException( + } catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { + throw new \Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException( __('Address id %1 does not exist.', [$addressId]) ); } if ($address->getCustomerId() != $customer->getId()) { - throw new GraphQlAuthorizationException( + throw new \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException( __('Current customer does not have permission to update address id %1', [$addressId]) ); } @@ -256,31 +226,31 @@ private function processCustomerAddressUpdate(CustomerInterface $customer, $addr * @param CustomerInterface $customer * @param int $addressId * @return bool - * @throws GraphQlAuthorizationException - * @throws GraphQlNoSuchEntityException + * @throws \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException + * @throws \Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException */ private function processCustomerAddressDelete(CustomerInterface $customer, $addressId) { try { /** @var AddressInterface $address */ $address = $this->addressRepositoryInterface->getById($addressId); - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException( + } catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { + throw new \Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException( __('Address id %1 does not exist.', [$addressId]) ); } if ($address->getCustomerId() != $customer->getId()) { - throw new GraphQlAuthorizationException( + throw new \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException( __('Current customer does not have permission to delete address id %1', [$addressId]) ); } if ($address->isDefaultBilling()) { - throw new GraphQlAuthorizationException( + throw new \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException( __('Customer Address %1 is set as default billing address and can not be deleted', [$addressId]) ); } if ($address->isDefaultShipping()) { - throw new GraphQlAuthorizationException( + throw new \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException( __('Customer Address %1 is set as default shipping address and can not be deleted', [$addressId]) ); } From 9041bdba067d618e1e382a59c4002c0488e7932f Mon Sep 17 00:00:00 2001 From: Pablo Fantini Date: Fri, 28 Sep 2018 12:28:10 -0300 Subject: [PATCH 026/704] GraphQL: Add missing module depenency and add input checker on address update --- .../Model/Resolver/Address.php | 121 ++++++++++-------- .../Magento/CustomerGraphQl/composer.json | 1 + .../Magento/CustomerGraphQl/etc/module.xml | 1 + 3 files changed, 72 insertions(+), 51 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php index 185830475ad3c..773f084bd2bc2 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php @@ -8,9 +8,9 @@ namespace Magento\CustomerGraphQl\Model\Resolver; use Magento\Authorization\Model\UserContextInterface; -use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\AddressMetadataManagementInterface; use Magento\Customer\Api\Data\AddressInterfaceFactory; use Magento\Customer\Api\Data\AddressInterface; use Magento\Framework\Api\DataObjectHelper; @@ -19,18 +19,16 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\CustomerGraphQl\Model\Resolver\Address\AddressDataProvider; use Magento\Eav\Model\Config; +use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Exception\NoSuchEntityException; /** * Customers Address, used for GraphQL request processing. */ class Address implements ResolverInterface { - /** - * Input data key - */ - const CUSTOM_ATTRIBUTE_KEY = 'custom_attributes'; - const EXTENSION_ATTRIBUTE_KEY = 'extension_attributes'; - /** * Mutation Address type */ @@ -38,11 +36,6 @@ class Address implements ResolverInterface const MUTATION_ADDRESS_UPDATE = 'customerAddressUpdate'; const MUTATION_ADDRESS_DELETE = 'customerAddressDelete'; - /** - * @var CustomerRepositoryInterface - */ - private $customerRepositoryInterface; - /** * @var AddressRepositoryInterface */ @@ -64,7 +57,7 @@ class Address implements ResolverInterface private $addressDataProvider; /** - * @var \Magento\Framework\Api\DataObjectHelper + * @var DataObjectHelper */ private $dataObjectHelper; @@ -74,7 +67,6 @@ class Address implements ResolverInterface private $addressAttributes; /** - * @param CustomerRepositoryInterface $customerRepositoryInterface * @param AddressRepositoryInterface $addressRepositoryInterface * @param AddressInterfaceFactory $addressInterfaceFactory * @param Config $eavConfig @@ -82,21 +74,19 @@ class Address implements ResolverInterface * @param DataObjectHelper $dataObjectHelper */ public function __construct( - CustomerRepositoryInterface $customerRepositoryInterface, AddressRepositoryInterface $addressRepositoryInterface, AddressInterfaceFactory $addressInterfaceFactory, Config $eavConfig, AddressDataProvider $addressDataProvider, DataObjectHelper $dataObjectHelper ) { - $this->customerRepositoryInterface = $customerRepositoryInterface; $this->addressRepositoryInterface = $addressRepositoryInterface; $this->addressInterfaceFactory = $addressInterfaceFactory; $this->eavConfig = $eavConfig; $this->addressDataProvider = $addressDataProvider; $this->dataObjectHelper = $dataObjectHelper; $this->addressAttributes = $this->eavConfig->getEntityAttributes( - \Magento\Customer\Api\AddressMetadataManagementInterface::ENTITY_TYPE_ADDRESS + AddressMetadataManagementInterface::ENTITY_TYPE_ADDRESS ); } @@ -112,40 +102,58 @@ public function resolve( ) { /** @var \Magento\Framework\GraphQl\Query\Resolver\ContextInterface $context */ if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { - throw new \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException( + throw new GraphQlAuthorizationException( __( 'Current customer does not have access to the resource "%1"', - [\Magento\Customer\Model\Customer::ENTITY] + [AddressMetadataManagementInterface::ENTITY_TYPE_ADDRESS] ) ); } - /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ - $customer = $this->customerRepositoryInterface->getById($context->getUserId()); + $customerId = $context->getUserId(); switch ($field->getName()) { case self::MUTATION_ADDRESS_CREATE: return $this->addressDataProvider->processCustomerAddress( - $this->processCustomerAddressCreate($customer, $args['input']) + $this->processCustomerAddressCreate($customerId, $args['input']) ); case self::MUTATION_ADDRESS_UPDATE: return $this->addressDataProvider->processCustomerAddress( - $this->processCustomerAddressUpdate($customer, $args['id'], $args['input']) + $this->processCustomerAddressUpdate($customerId, $args['id'], $args['input']) ); case self::MUTATION_ADDRESS_DELETE: - return $this->processCustomerAddressDelete($customer, $args['id']); + return $this->processCustomerAddressDelete($customerId, $args['id']); default: return []; } } /** - * Get input address attribute errors + * Get new address attribute input errors + * + * @param array $addressInput + * @return bool|string + */ + private function getNewAddressInputError(array $addressInput) + { + foreach ($this->addressAttributes as $attributeName => $attributeInfo) { + if ($attributeInfo->getIsRequired() + && (!isset($addressInput[$attributeName]) || empty($addressInput[$attributeName]))) { + return $attributeName; + } + } + return false; + } + + /** + * Get update address attribute input errors + * * @param array $addressInput * @return bool|string */ - private function getAddressInputError(array $addressInput) + private function getUpdateAddressInputError(array $addressInput) { foreach ($this->addressAttributes as $attributeName => $attributeInfo) { - if ($attributeInfo->getIsRequired() && !isset($addressInput[$attributeName])) { + if ($attributeInfo->getIsRequired() + && (isset($addressInput[$attributeName]) && empty($addressInput[$attributeName]))) { return $attributeName; } } @@ -154,6 +162,7 @@ private function getAddressInputError(array $addressInput) /** * Add $addressInput array information to a $address object + * * @param AddressInterface $address * @param array $addressInput * @return AddressInterface @@ -170,16 +179,17 @@ private function fillAddress(AddressInterface $address, array $addressInput) : A /** * Process customer address create - * @param CustomerInterface $customer + * + * @param int $customerId * @param array $addressInput * @return AddressInterface - * @throws \Magento\Framework\GraphQl\Exception\GraphQlInputException + * @throws GraphQlInputException */ - private function processCustomerAddressCreate(CustomerInterface $customer, array $addressInput) : AddressInterface + private function processCustomerAddressCreate($customerId, array $addressInput) : AddressInterface { - $errorInput = $this->getAddressInputError($addressInput); + $errorInput = $this->getNewAddressInputError($addressInput); if ($errorInput) { - throw new \Magento\Framework\GraphQl\Exception\GraphQlInputException( + throw new GraphQlInputException( __('Required parameter %1 is missing', [$errorInput]) ); } @@ -188,34 +198,42 @@ private function processCustomerAddressCreate(CustomerInterface $customer, array $this->addressInterfaceFactory->create(), $addressInput ); - $newAddress->setCustomerId($customer->getId()); + $newAddress->setCustomerId($customerId); return $this->addressRepositoryInterface->save($newAddress); } /** * Process customer address update - * @param CustomerInterface $customer + * + * @param int $customerId * @param int $addressId * @param array $addressInput * @return AddressInterface - * @throws \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException - * @throws \Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException + * @throws GraphQlAuthorizationException + * @throws GraphQlNoSuchEntityException + * @throws GraphQlInputException */ - private function processCustomerAddressUpdate(CustomerInterface $customer, $addressId, array $addressInput) + private function processCustomerAddressUpdate($customerId, $addressId, array $addressInput) { try { /** @var AddressInterface $address */ $address = $this->addressRepositoryInterface->getById($addressId); - } catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { - throw new \Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException( + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException( __('Address id %1 does not exist.', [$addressId]) ); } - if ($address->getCustomerId() != $customer->getId()) { - throw new \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException( + if ($address->getCustomerId() != $customerId) { + throw new GraphQlAuthorizationException( __('Current customer does not have permission to update address id %1', [$addressId]) ); } + $errorInput = $this->getUpdateAddressInputError($addressInput); + if ($errorInput) { + throw new GraphQlInputException( + __('Required parameter %1 is missing', [$errorInput]) + ); + } return $this->addressRepositoryInterface->save( $this->fillAddress($address, $addressInput) ); @@ -223,34 +241,35 @@ private function processCustomerAddressUpdate(CustomerInterface $customer, $addr /** * Process customer address delete - * @param CustomerInterface $customer + * + * @param int $customerId * @param int $addressId * @return bool - * @throws \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException - * @throws \Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException + * @throws GraphQlAuthorizationException + * @throws GraphQlNoSuchEntityException */ - private function processCustomerAddressDelete(CustomerInterface $customer, $addressId) + private function processCustomerAddressDelete($customerId, $addressId) { try { /** @var AddressInterface $address */ $address = $this->addressRepositoryInterface->getById($addressId); - } catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { - throw new \Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException( + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException( __('Address id %1 does not exist.', [$addressId]) ); } - if ($address->getCustomerId() != $customer->getId()) { - throw new \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException( + if ($customerId != $address->getCustomerId()) { + throw new GraphQlAuthorizationException( __('Current customer does not have permission to delete address id %1', [$addressId]) ); } if ($address->isDefaultBilling()) { - throw new \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException( + throw new GraphQlAuthorizationException( __('Customer Address %1 is set as default billing address and can not be deleted', [$addressId]) ); } if ($address->isDefaultShipping()) { - throw new \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException( + throw new GraphQlAuthorizationException( __('Customer Address %1 is set as default shipping address and can not be deleted', [$addressId]) ); } diff --git a/app/code/Magento/CustomerGraphQl/composer.json b/app/code/Magento/CustomerGraphQl/composer.json index c26c83c95be38..f535dea6787a5 100644 --- a/app/code/Magento/CustomerGraphQl/composer.json +++ b/app/code/Magento/CustomerGraphQl/composer.json @@ -7,6 +7,7 @@ "magento/module-customer": "*", "magento/module-authorization": "*", "magento/module-integration": "*", + "magento/module-eav": "*", "magento/framework": "*" }, "suggest": { diff --git a/app/code/Magento/CustomerGraphQl/etc/module.xml b/app/code/Magento/CustomerGraphQl/etc/module.xml index bde93c6276500..659171ce80735 100644 --- a/app/code/Magento/CustomerGraphQl/etc/module.xml +++ b/app/code/Magento/CustomerGraphQl/etc/module.xml @@ -11,6 +11,7 @@ + From 91275d606be59a30b6dc327309c7d7faa2cdefa5 Mon Sep 17 00:00:00 2001 From: Dmytro Cheshun Date: Sat, 29 Sep 2018 08:59:50 +0300 Subject: [PATCH 027/704] Revert "Fix the minor code styling issue" This reverts commit ebd39d36bb3422eec41a939f5c20a9b1ae2efde3. --- .../Magento/Customer/Model/AccountManagement.php | 10 +++++----- .../Test/Unit/Model/AccountManagementTest.php | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index 99aa494e9981f..c2f46b24985af 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -259,7 +259,7 @@ class AccountManagement implements AccountManagementInterface /** * @var CollectionFactory */ - private $visitorColFactory; + private $visitorCollectionFactory; /** * @var DataObjectProcessor @@ -360,7 +360,7 @@ class AccountManagement implements AccountManagementInterface * @param AccountConfirmation|null $accountConfirmation * @param SessionManagerInterface|null $sessionManager * @param SaveHandlerInterface|null $saveHandler - * @param CollectionFactory|null $visitorColFactory + * @param CollectionFactory|null $visitorCollectionFactory * @param AddressRegistry|null $addressRegistry * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -393,7 +393,7 @@ public function __construct( AccountConfirmation $accountConfirmation = null, SessionManagerInterface $sessionManager = null, SaveHandlerInterface $saveHandler = null, - CollectionFactory $visitorColFactory = null, + CollectionFactory $visitorCollectionFactory = null, AddressRegistry $addressRegistry = null ) { $this->customerFactory = $customerFactory; @@ -428,7 +428,7 @@ public function __construct( ?: ObjectManager::getInstance()->get(SessionManagerInterface::class); $this->saveHandler = $saveHandler ?: ObjectManager::getInstance()->get(SaveHandlerInterface::class); - $this->visitorColFactory = $visitorColFactory + $this->visitorCollectionFactory = $visitorCollectionFactory ?: ObjectManager::getInstance()->get(CollectionFactory::class); $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class); @@ -1480,7 +1480,7 @@ private function destroyCustomerSessions($customerId) $activeSessionsTime = $dateTime->setTimestamp($dateTime->getTimestamp() - $sessionLifetime) ->format(DateTime::DATETIME_PHP_FORMAT); /** @var \Magento\Customer\Model\ResourceModel\Visitor\Collection $visitorCollection */ - $visitorCollection = $this->visitorColFactory->create(); + $visitorCollection = $this->visitorCollectionFactory->create(); $visitorCollection->addFieldToFilter('customer_id', $customerId); $visitorCollection->addFieldToFilter('last_visit_at', ['from' => $activeSessionsTime]); $visitorCollection->addFieldToFilter('session_id', ['neq' => $this->sessionManager->getSessionId()]); diff --git a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php index b1e1967aaa630..27880e44a7239 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php @@ -135,7 +135,7 @@ class AccountManagementTest extends \PHPUnit\Framework\TestCase /** * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Customer\Model\ResourceModel\Visitor\CollectionFactory */ - private $visitorColFactory; + private $visitorCollectionFactory; /** * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Session\SaveHandlerInterface @@ -200,7 +200,7 @@ protected function setUp() $this->dateTimeFactory = $this->createMock(DateTimeFactory::class); $this->accountConfirmation = $this->createMock(AccountConfirmation::class); - $this->visitorColFactory = $this->getMockBuilder( + $this->visitorCollectionFactory = $this->getMockBuilder( \Magento\Customer\Model\ResourceModel\Visitor\CollectionFactory::class ) ->disableOriginalConstructor() @@ -244,7 +244,7 @@ protected function setUp() 'accountConfirmation' => $this->accountConfirmation, 'sessionManager' => $this->sessionManager, 'saveHandler' => $this->saveHandler, - 'visitorColFactory' => $this->visitorColFactory, + 'visitorCollectionFactory' => $this->visitorCollectionFactory, 'addressRegistry' => $this->addressRegistryMock, ] ); @@ -1385,7 +1385,7 @@ private function reInitModel() $this->sessionManager = $this->getMockBuilder(\Magento\Framework\Session\SessionManagerInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->visitorColFactory = $this->getMockBuilder( + $this->visitorCollectionFactory = $this->getMockBuilder( \Magento\Customer\Model\ResourceModel\Visitor\CollectionFactory::class ) ->disableOriginalConstructor() @@ -1431,7 +1431,7 @@ private function reInitModel() 'stringHelper' => $this->string, 'scopeConfig' => $this->scopeConfig, 'sessionManager' => $this->sessionManager, - 'visitorColFactory' => $this->visitorColFactory, + 'visitorCollectionFactory' => $this->visitorCollectionFactory, 'saveHandler' => $this->saveHandler, 'encryptor' => $this->encryptor, 'dataProcessor' => $this->dataObjectProcessor, @@ -1530,7 +1530,7 @@ public function testChangePassword() ->disableOriginalConstructor()->setMethods(['addFieldToFilter', 'getItems'])->getMock(); $visitorCollection->expects($this->atLeastOnce())->method('addFieldToFilter')->willReturnSelf(); $visitorCollection->expects($this->atLeastOnce())->method('getItems')->willReturn([$visitor, $visitor]); - $this->visitorColFactory->expects($this->atLeastOnce())->method('create') + $this->visitorCollectionFactory->expects($this->atLeastOnce())->method('create') ->willReturn($visitorCollection); $this->saveHandler->expects($this->atLeastOnce())->method('destroy') ->withConsecutive( @@ -1584,7 +1584,7 @@ function ($string) { ->disableOriginalConstructor()->setMethods(['addFieldToFilter', 'getItems'])->getMock(); $visitorCollection->expects($this->atLeastOnce())->method('addFieldToFilter')->willReturnSelf(); $visitorCollection->expects($this->atLeastOnce())->method('getItems')->willReturn([$visitor, $visitor]); - $this->visitorColFactory->expects($this->atLeastOnce())->method('create') + $this->visitorCollectionFactory->expects($this->atLeastOnce())->method('create') ->willReturn($visitorCollection); $this->saveHandler->expects($this->atLeastOnce())->method('destroy') ->withConsecutive( From 08ea73c66f41863dc5f75ec60a25e1ba517cbb6b Mon Sep 17 00:00:00 2001 From: Danny Verkade Date: Sun, 30 Sep 2018 21:28:54 +0800 Subject: [PATCH 028/704] Removed negative price from product options test file. --- .../Magento/Catalog/Api/_files/product_options.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php index 955322929762d..e61e6061f4fc2 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php @@ -153,14 +153,4 @@ 'price_type' => 'fixed', 'sku' => 'time option sku', ], - [ - 'title' => 'test negative price', - 'type' => 'field', - 'sort_order' => 1, - 'is_require' => 1, - 'price' => -10, - 'price_type' => 'fixed', - 'sku' => 'sku1', - 'max_characters' => 10, - ], -]; +]; \ No newline at end of file From cfc82be0b908fa444f79d14e90c3abe856ae8727 Mon Sep 17 00:00:00 2001 From: Pablo Fantini Date: Mon, 1 Oct 2018 19:37:03 -0300 Subject: [PATCH 029/704] GraphQL-57: Refactor Address resolve and change functional test --- .../Model/Resolver/Address.php | 278 -------------- .../Address/AddressConfigProvider.php | 76 ++++ .../Model/Resolver/AddressCreate.php | 134 +++++++ .../Model/Resolver/AddressDelete.php | 100 +++++ .../Model/Resolver/AddressUpdate.php | 148 ++++++++ .../CustomerGraphQl/etc/schema.graphqls | 6 +- .../Customer/CustomerAddressCreateTest.php | 300 +++++++++++++++ .../Customer/CustomerAddressDeleteTest.php | 184 ++++++++++ ...Test.php => CustomerAddressUpdateTest.php} | 342 ++++-------------- 9 files changed, 1013 insertions(+), 555 deletions(-) delete mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php create mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressConfigProvider.php create mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/AddressCreate.php create mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/AddressDelete.php create mode 100644 app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressCreateTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressDeleteTest.php rename dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/{CustomerAddressTest.php => CustomerAddressUpdateTest.php} (51%) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php deleted file mode 100644 index 773f084bd2bc2..0000000000000 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address.php +++ /dev/null @@ -1,278 +0,0 @@ -addressRepositoryInterface = $addressRepositoryInterface; - $this->addressInterfaceFactory = $addressInterfaceFactory; - $this->eavConfig = $eavConfig; - $this->addressDataProvider = $addressDataProvider; - $this->dataObjectHelper = $dataObjectHelper; - $this->addressAttributes = $this->eavConfig->getEntityAttributes( - AddressMetadataManagementInterface::ENTITY_TYPE_ADDRESS - ); - } - - /** - * @inheritdoc - */ - public function resolve( - Field $field, - $context, - ResolveInfo $info, - array $value = null, - array $args = null - ) { - /** @var \Magento\Framework\GraphQl\Query\Resolver\ContextInterface $context */ - if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { - throw new GraphQlAuthorizationException( - __( - 'Current customer does not have access to the resource "%1"', - [AddressMetadataManagementInterface::ENTITY_TYPE_ADDRESS] - ) - ); - } - $customerId = $context->getUserId(); - switch ($field->getName()) { - case self::MUTATION_ADDRESS_CREATE: - return $this->addressDataProvider->processCustomerAddress( - $this->processCustomerAddressCreate($customerId, $args['input']) - ); - case self::MUTATION_ADDRESS_UPDATE: - return $this->addressDataProvider->processCustomerAddress( - $this->processCustomerAddressUpdate($customerId, $args['id'], $args['input']) - ); - case self::MUTATION_ADDRESS_DELETE: - return $this->processCustomerAddressDelete($customerId, $args['id']); - default: - return []; - } - } - - /** - * Get new address attribute input errors - * - * @param array $addressInput - * @return bool|string - */ - private function getNewAddressInputError(array $addressInput) - { - foreach ($this->addressAttributes as $attributeName => $attributeInfo) { - if ($attributeInfo->getIsRequired() - && (!isset($addressInput[$attributeName]) || empty($addressInput[$attributeName]))) { - return $attributeName; - } - } - return false; - } - - /** - * Get update address attribute input errors - * - * @param array $addressInput - * @return bool|string - */ - private function getUpdateAddressInputError(array $addressInput) - { - foreach ($this->addressAttributes as $attributeName => $attributeInfo) { - if ($attributeInfo->getIsRequired() - && (isset($addressInput[$attributeName]) && empty($addressInput[$attributeName]))) { - return $attributeName; - } - } - return false; - } - - /** - * Add $addressInput array information to a $address object - * - * @param AddressInterface $address - * @param array $addressInput - * @return AddressInterface - */ - private function fillAddress(AddressInterface $address, array $addressInput) : AddressInterface - { - $this->dataObjectHelper->populateWithArray( - $address, - $addressInput, - AddressInterface::class - ); - return $address; - } - - /** - * Process customer address create - * - * @param int $customerId - * @param array $addressInput - * @return AddressInterface - * @throws GraphQlInputException - */ - private function processCustomerAddressCreate($customerId, array $addressInput) : AddressInterface - { - $errorInput = $this->getNewAddressInputError($addressInput); - if ($errorInput) { - throw new GraphQlInputException( - __('Required parameter %1 is missing', [$errorInput]) - ); - } - /** @var AddressInterface $newAddress */ - $newAddress = $this->fillAddress( - $this->addressInterfaceFactory->create(), - $addressInput - ); - $newAddress->setCustomerId($customerId); - return $this->addressRepositoryInterface->save($newAddress); - } - - /** - * Process customer address update - * - * @param int $customerId - * @param int $addressId - * @param array $addressInput - * @return AddressInterface - * @throws GraphQlAuthorizationException - * @throws GraphQlNoSuchEntityException - * @throws GraphQlInputException - */ - private function processCustomerAddressUpdate($customerId, $addressId, array $addressInput) - { - try { - /** @var AddressInterface $address */ - $address = $this->addressRepositoryInterface->getById($addressId); - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException( - __('Address id %1 does not exist.', [$addressId]) - ); - } - if ($address->getCustomerId() != $customerId) { - throw new GraphQlAuthorizationException( - __('Current customer does not have permission to update address id %1', [$addressId]) - ); - } - $errorInput = $this->getUpdateAddressInputError($addressInput); - if ($errorInput) { - throw new GraphQlInputException( - __('Required parameter %1 is missing', [$errorInput]) - ); - } - return $this->addressRepositoryInterface->save( - $this->fillAddress($address, $addressInput) - ); - } - - /** - * Process customer address delete - * - * @param int $customerId - * @param int $addressId - * @return bool - * @throws GraphQlAuthorizationException - * @throws GraphQlNoSuchEntityException - */ - private function processCustomerAddressDelete($customerId, $addressId) - { - try { - /** @var AddressInterface $address */ - $address = $this->addressRepositoryInterface->getById($addressId); - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException( - __('Address id %1 does not exist.', [$addressId]) - ); - } - if ($customerId != $address->getCustomerId()) { - throw new GraphQlAuthorizationException( - __('Current customer does not have permission to delete address id %1', [$addressId]) - ); - } - if ($address->isDefaultBilling()) { - throw new GraphQlAuthorizationException( - __('Customer Address %1 is set as default billing address and can not be deleted', [$addressId]) - ); - } - if ($address->isDefaultShipping()) { - throw new GraphQlAuthorizationException( - __('Customer Address %1 is set as default shipping address and can not be deleted', [$addressId]) - ); - } - return $this->addressRepositoryInterface->delete($address); - } -} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressConfigProvider.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressConfigProvider.php new file mode 100644 index 0000000000000..8f16d2890f5e1 --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressConfigProvider.php @@ -0,0 +1,76 @@ +eavConfig = $eavConfig; + $this->dataObjectHelper = $dataObjectHelper; + $this->addressAttributes = $this->eavConfig->getEntityAttributes( + AddressMetadataManagementInterface::ENTITY_TYPE_ADDRESS + ); + } + + /** + * Add $addressInput array information to a $address object + * + * @param AddressInterface $address + * @param array $addressInput + * @return AddressInterface + */ + public function fillAddress(AddressInterface $address, array $addressInput) : AddressInterface + { + $this->dataObjectHelper->populateWithArray( + $address, + $addressInput, + AddressInterface::class + ); + return $address; + } + + /** + * Get address field configuration + * + * @return array + */ + public function getAddressAttributes() : array + { + return $this->addressAttributes; + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressCreate.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressCreate.php new file mode 100644 index 0000000000000..0420a9d91049a --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressCreate.php @@ -0,0 +1,134 @@ +addressRepositoryInterface = $addressRepositoryInterface; + $this->addressInterfaceFactory = $addressInterfaceFactory; + $this->addressDataProvider = $addressDataProvider; + $this->addressConfigProvider = $addressConfigProvider; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + /** @var \Magento\Framework\GraphQl\Query\Resolver\ContextInterface $context */ + if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { + throw new GraphQlAuthorizationException( + __( + 'Current customer does not have access to the resource "%1"', + [AddressMetadataManagementInterface::ENTITY_TYPE_ADDRESS] + ) + ); + } + $customerId = $context->getUserId(); + return $this->addressDataProvider->processCustomerAddress( + $this->processCustomerAddressCreate($customerId, $args['input']) + ); + } + + /** + * Get new address attribute input errors + * + * @param array $addressInput + * @return bool|string + */ + public function getInputError(array $addressInput) + { + $attributes = $this->addressConfigProvider->getAddressAttributes(); + foreach ($attributes as $attributeName => $attributeInfo) { + if ($attributeInfo->getIsRequired() + && (!isset($addressInput[$attributeName]) || empty($addressInput[$attributeName]))) { + return $attributeName; + } + } + return false; + } + + /** + * Process customer address create + * + * @param int $customerId + * @param array $addressInput + * @return AddressInterface + * @throws GraphQlInputException + */ + private function processCustomerAddressCreate($customerId, array $addressInput) : AddressInterface + { + $errorInput = $this->getInputError($addressInput); + if ($errorInput) { + throw new GraphQlInputException( + __('Required parameter %1 is missing', [$errorInput]) + ); + } + /** @var AddressInterface $newAddress */ + $newAddress = $this->addressConfigProvider->fillAddress( + $this->addressInterfaceFactory->create(), + $addressInput + ); + $newAddress->setCustomerId($customerId); + return $this->addressRepositoryInterface->save($newAddress); + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressDelete.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressDelete.php new file mode 100644 index 0000000000000..540d7645cc657 --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressDelete.php @@ -0,0 +1,100 @@ +addressRepositoryInterface = $addressRepositoryInterface; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + /** @var \Magento\Framework\GraphQl\Query\Resolver\ContextInterface $context */ + if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { + throw new GraphQlAuthorizationException( + __( + 'Current customer does not have access to the resource "%1"', + [AddressMetadataManagementInterface::ENTITY_TYPE_ADDRESS] + ) + ); + } + $customerId = $context->getUserId(); + return $this->processCustomerAddressDelete($customerId, $args['id']); + } + + /** + * Process customer address delete + * + * @param int $customerId + * @param int $addressId + * @return bool + * @throws GraphQlAuthorizationException + * @throws GraphQlNoSuchEntityException + */ + private function processCustomerAddressDelete($customerId, $addressId) + { + try { + /** @var AddressInterface $address */ + $address = $this->addressRepositoryInterface->getById($addressId); + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException( + __('Address id %1 does not exist.', [$addressId]) + ); + } + if ($customerId != $address->getCustomerId()) { + throw new GraphQlAuthorizationException( + __('Current customer does not have permission to delete address id %1', [$addressId]) + ); + } + if ($address->isDefaultBilling()) { + throw new GraphQlAuthorizationException( + __('Customer Address %1 is set as default billing address and can not be deleted', [$addressId]) + ); + } + if ($address->isDefaultShipping()) { + throw new GraphQlAuthorizationException( + __('Customer Address %1 is set as default shipping address and can not be deleted', [$addressId]) + ); + } + return $this->addressRepositoryInterface->delete($address); + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php new file mode 100644 index 0000000000000..800ba5f2804f4 --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php @@ -0,0 +1,148 @@ +addressRepositoryInterface = $addressRepositoryInterface; + $this->addressInterfaceFactory = $addressInterfaceFactory; + $this->addressDataProvider = $addressDataProvider; + $this->addressConfigProvider = $addressConfigProvider; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + /** @var \Magento\Framework\GraphQl\Query\Resolver\ContextInterface $context */ + if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) { + throw new GraphQlAuthorizationException( + __( + 'Current customer does not have access to the resource "%1"', + [AddressMetadataManagementInterface::ENTITY_TYPE_ADDRESS] + ) + ); + } + $customerId = $context->getUserId(); + return $this->addressDataProvider->processCustomerAddress( + $this->processCustomerAddressUpdate($customerId, $args['id'], $args['input']) + ); + } + + /** + * Get update address attribute input errors + * + * @param array $addressInput + * @return bool|string + */ + private function getInputError(array $addressInput) + { + $attributes = $this->addressConfigProvider->getAddressAttributes(); + foreach ($attributes as $attributeName => $attributeInfo) { + if ($attributeInfo->getIsRequired() + && (isset($addressInput[$attributeName]) && empty($addressInput[$attributeName]))) { + return $attributeName; + } + } + return false; + } + + /** + * Process customer address update + * + * @param int $customerId + * @param int $addressId + * @param array $addressInput + * @return AddressInterface + * @throws GraphQlAuthorizationException + * @throws GraphQlNoSuchEntityException + * @throws GraphQlInputException + */ + private function processCustomerAddressUpdate($customerId, $addressId, array $addressInput) + { + try { + /** @var AddressInterface $address */ + $address = $this->addressRepositoryInterface->getById($addressId); + } catch (NoSuchEntityException $exception) { + throw new GraphQlNoSuchEntityException( + __('Address id %1 does not exist.', [$addressId]) + ); + } + if ($address->getCustomerId() != $customerId) { + throw new GraphQlAuthorizationException( + __('Current customer does not have permission to update address id %1', [$addressId]) + ); + } + $errorInput = $this->getInputError($addressInput); + if ($errorInput) { + throw new GraphQlInputException( + __('Required parameter %1 is missing', [$errorInput]) + ); + } + return $this->addressRepositoryInterface->save( + $this->addressConfigProvider->fillAddress($address, $addressInput) + ); + } +} diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index c53bfa645d807..3adc92a02d7db 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -8,9 +8,9 @@ type Query { type Mutation { generateCustomerToken(email: String!, password: String!): CustomerToken @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\Customer\\Account\\GenerateCustomerToken") @doc(description:"Retrieve Customer token") changeCustomerPassword(currentPassword: String!, newPassword: String!): Customer @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\Customer\\Account\\ChangePassword") @doc(description:"Changes password for logged in customer") - customerAddressCreate(input: CustomerAddressInput!): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\Address") @doc(description: "Create customer address") - customerAddressUpdate(id: Int!, input: CustomerAddressInput): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\Address") @doc(description: "Update customer address") - customerAddressDelete(id: Int!): Boolean @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\Address") @doc(description: "Delete customer address") + customerAddressCreate(input: CustomerAddressInput!): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\AddressCreate") @doc(description: "Create customer address") + customerAddressUpdate(id: Int!, input: CustomerAddressInput): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\AddressUpdate") @doc(description: "Update customer address") + customerAddressDelete(id: Int!): Boolean @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\AddressDelete") @doc(description: "Delete customer address") } input CustomerAddressInput { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressCreateTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressCreateTest.php new file mode 100644 index 0000000000000..2db574c508827 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressCreateTest.php @@ -0,0 +1,300 @@ + [ + 'region' => 'Alaska', + 'region_id' => 4, + 'region_code' => 'AK' + ], + 'region_id' => 4, + 'country_id' => 'US', + 'street' => ['Line 1 Street', 'Line 2'], + 'company' => 'Company name', + 'telephone' => '123456789', + 'fax' => '123123123', + 'postcode' => '7777', + 'city' => 'City Name', + 'firstname' => 'Adam', + 'lastname' => 'Phillis', + 'middlename' => 'A', + 'prefix' => 'Mr.', + 'suffix' => 'Jr.', + 'vat_id' => '1', + 'default_shipping' => false, + 'default_billing' => false + ]; + $defaultShippingText = $newAddress['default_shipping'] ? "true": "false"; + $defaultBillingText = $newAddress['default_billing'] ? "true": "false"; + $mutation + = <<get(CustomerTokenServiceInterface::class); + $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get($userName); + $response = $this->graphQlQuery($mutation, [], '', $headerMap); + $this->assertArrayHasKey('customerAddressCreate', $response); + $this->assertArrayHasKey('customer_id', $response['customerAddressCreate']); + $this->assertEquals($customer->getId(), $response['customerAddressCreate']['customer_id']); + $this->assertArrayHasKey('id', $response['customerAddressCreate']); + /** @var AddressRepositoryInterface $addressRepository */ + $addressRepository = ObjectManager::getInstance()->get(AddressRepositoryInterface::class); + $addressId = $response['customerAddressCreate']['id']; + $address = $addressRepository->getById($addressId); + $this->assertEquals($address->getId(), $response['customerAddressCreate']['id']); + $this->assertCustomerAddressesFields($address, $response['customerAddressCreate']); + $this->assertCustomerAddressesFields($address, $newAddress); + } + + /** + * Verify customers without credentials create new address + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testAddCustomerAddressWithoutCredentials() + { + $mutation + = <<expectException(\Exception::class); + $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . + 'Current customer does not have access to the resource "customer_address"'); + $this->graphQlQuery($mutation); + } + + /** + * Verify customers with valid credentials create new address + * with missing required Firstname attribute + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testAddCustomerAddressWithMissingAttributeWithValidCredentials() + { + $newAddress = [ + 'region' => [ + 'region' => 'Alaska', + 'region_id' => 4, + 'region_code' => 'AK' + ], + 'region_id' => 4, + 'country_id' => 'US', + 'street' => ['Line 1 Street', 'Line 2'], + 'company' => 'Company name', + 'telephone' => '123456789', + 'fax' => '123123123', + 'postcode' => '7777', + 'city' => 'City Name', + 'firstname' => '', //empty firstname + 'lastname' => 'Phillis', + 'middlename' => 'A', + 'prefix' => 'Mr.', + 'suffix' => 'Jr.', + 'vat_id' => '1', + 'default_shipping' => false, + 'default_billing' => false + ]; + $defaultShippingText = $newAddress['default_shipping'] ? "true": "false"; + $defaultBillingText = $newAddress['default_billing'] ? "true": "false"; + $mutation + = <<get(CustomerTokenServiceInterface::class); + $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + $this->expectException(\Exception::class); + $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . + 'Required parameter firstname is missing'); + $this->graphQlQuery($mutation, [], '', $headerMap); + } + + /** + * Verify the fields for Customer address + * + * @param \Magento\Customer\Api\Data\AddressInterface $address + * @param array $actualResponse + */ + private function assertCustomerAddressesFields($address, $actualResponse) + { + /** @var $addresses */ + $assertionMap = [ + ['response_field' => 'region_id', 'expected_value' => $address->getRegionId()], + ['response_field' => 'country_id', 'expected_value' => $address->getCountryId()], + ['response_field' => 'street', 'expected_value' => $address->getStreet()], + ['response_field' => 'company', 'expected_value' => $address->getCompany()], + ['response_field' => 'telephone', 'expected_value' => $address->getTelephone()], + ['response_field' => 'fax', 'expected_value' => $address->getFax()], + ['response_field' => 'postcode', 'expected_value' => $address->getPostcode()], + ['response_field' => 'city', 'expected_value' => $address->getCity()], + ['response_field' => 'firstname', 'expected_value' => $address->getFirstname()], + ['response_field' => 'lastname', 'expected_value' => $address->getLastname()], + ['response_field' => 'middlename', 'expected_value' => $address->getMiddlename()], + ['response_field' => 'prefix', 'expected_value' => $address->getPrefix()], + ['response_field' => 'suffix', 'expected_value' => $address->getSuffix()], + ['response_field' => 'vat_id', 'expected_value' => $address->getVatId()], + ['response_field' => 'default_shipping', 'expected_value' => (bool)$address->isDefaultShipping()], + ['response_field' => 'default_billing', 'expected_value' => (bool)$address->isDefaultBilling()], + ]; + $this->assertResponseFields($actualResponse, $assertionMap); + $this->assertTrue(is_array([$actualResponse['region']]), "region field must be of an array type."); + $assertionRegionMap = [ + ['response_field' => 'region', 'expected_value' => $address->getRegion()->getRegion()], + ['response_field' => 'region_code', 'expected_value' => $address->getRegion()->getRegionCode()], + ['response_field' => 'region_id', 'expected_value' => $address->getRegion()->getRegionId()] + ]; + $this->assertResponseFields($actualResponse['region'], $assertionRegionMap); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressDeleteTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressDeleteTest.php new file mode 100644 index 0000000000000..e1371e47aab4a --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressDeleteTest.php @@ -0,0 +1,184 @@ +get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get($userName); + /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ + $addresses = $customer->getAddresses(); + /** @var \Magento\Customer\Api\Data\AddressInterface $address */ + $address = end($addresses); + $addressId = $address->getId(); + $mutation + = <<get(CustomerTokenServiceInterface::class); + $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + $response = $this->graphQlQuery($mutation, [], '', $headerMap); + $this->assertArrayHasKey('customerAddressDelete', $response); + $this->assertEquals(true, $response['customerAddressDelete']); + } + + /** + * Verify customers without credentials delete address + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_address.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testDeleteCustomerAddressWithoutCredentials() + { + $userName = 'customer@example.com'; + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get($userName); + /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ + $addresses = $customer->getAddresses(); + /** @var \Magento\Customer\Api\Data\AddressInterface $address */ + $address = current($addresses); + $addressId = $address->getId(); + $mutation + = <<expectException(\Exception::class); + $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . + 'Current customer does not have access to the resource "customer_address"'); + $this->graphQlQuery($mutation); + } + + /** + * Verify customers with valid credentials delete default shipping address + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testDeleteDefaultShippingCustomerAddressWithValidCredentials() + { + $userName = 'customer@example.com'; + $password = 'password'; + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get($userName); + /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ + $addresses = $customer->getAddresses(); + /** @var \Magento\Customer\Api\Data\AddressInterface $address */ + $address = end($addresses); + $address->setIsDefaultShipping(true); + $addressRepository = ObjectManager::getInstance()->get(AddressRepositoryInterface::class); + $addressRepository->save($address); + $addressId = $address->getId(); + $mutation + = <<get(CustomerTokenServiceInterface::class); + $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + $this->expectException(\Exception::class); + $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . + 'Customer Address ' . $addressId . ' is set as default shipping address and can not be deleted'); + $this->graphQlQuery($mutation, [], '', $headerMap); + } + + /** + * Verify customers with valid credentials delete default billing address + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testDeleteDefaultBillingCustomerAddressWithValidCredentials() + { + $userName = 'customer@example.com'; + $password = 'password'; + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get($userName); + /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ + $addresses = $customer->getAddresses(); + /** @var \Magento\Customer\Api\Data\AddressInterface $address */ + $address = end($addresses); + $address->setIsDefaultBilling(true); + $addressRepository = ObjectManager::getInstance()->get(AddressRepositoryInterface::class); + $addressRepository->save($address); + $addressId = $address->getId(); + $mutation + = <<get(CustomerTokenServiceInterface::class); + $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + $this->expectException(\Exception::class); + $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . + 'Customer Address ' . $addressId . ' is set as default billing address and can not be deleted'); + $this->graphQlQuery($mutation, [], '', $headerMap); + } + + /** + * Verify customers with valid credentials delete non exist address + * + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testDeleteNonExistCustomerAddressWithValidCredentials() + { + $userName = 'customer@example.com'; + $password = 'password'; + $mutation + = <<get(CustomerTokenServiceInterface::class); + $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); + $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; + $this->expectException(\Exception::class); + $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . + 'Address id 9999 does not exist.'); + $this->graphQlQuery($mutation, [], '', $headerMap); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressUpdateTest.php similarity index 51% rename from dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressTest.php rename to dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressUpdateTest.php index 6a1ea8934bfa9..f8149c9067038 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressUpdateTest.php @@ -14,18 +14,20 @@ use Magento\Integration\Api\CustomerTokenServiceInterface; use PHPUnit\Framework\TestResult; -class CustomerAddressTest extends GraphQlAbstract +class CustomerAddressUpdateTest extends GraphQlAbstract { - /** - * Verify customers with valid credentials create new address + * Verify customers with valid credentials update address * * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testAddCustomerAddressWithValidCredentials() + public function testUpdateCustomerAddressWithValidCredentials() { - $newAddress = [ + $userName = 'customer@example.com'; + $password = 'password'; + $updateAddress = [ 'region' => [ 'region' => 'Alaska', 'region_id' => 4, @@ -34,7 +36,7 @@ public function testAddCustomerAddressWithValidCredentials() 'region_id' => 4, 'country_id' => 'US', 'street' => ['Line 1 Street', 'Line 2'], - 'company' => 'Company name', + 'company' => 'Company Name', 'telephone' => '123456789', 'fax' => '123123123', 'postcode' => '7777', @@ -45,34 +47,42 @@ public function testAddCustomerAddressWithValidCredentials() 'prefix' => 'Mr.', 'suffix' => 'Jr.', 'vat_id' => '1', - 'default_shipping' => false, - 'default_billing' => false + 'default_shipping' => true, + 'default_billing' => true ]; - $defaultShippingText = $newAddress['default_shipping'] ? "true": "false"; - $defaultBillingText = $newAddress['default_billing'] ? "true": "false"; + $defaultShippingText = $updateAddress['default_shipping'] ? "true": "false"; + $defaultBillingText = $updateAddress['default_billing'] ? "true": "false"; + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get($userName); + /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ + $addresses = $customer->getAddresses(); + /** @var \Magento\Customer\Api\Data\AddressInterface $address */ + $address = current($addresses); + $addressId = $address->getId(); $mutation = <<get(CustomerTokenServiceInterface::class); $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); @@ -112,65 +120,64 @@ public function testAddCustomerAddressWithValidCredentials() $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); $customer = $customerRepository->get($userName); $response = $this->graphQlQuery($mutation, [], '', $headerMap); - $this->assertArrayHasKey('customerAddressCreate', $response); - $this->assertArrayHasKey('customer_id', $response['customerAddressCreate']); - $this->assertEquals($customer->getId(), $response['customerAddressCreate']['customer_id']); - $this->assertArrayHasKey('id', $response['customerAddressCreate']); + $this->assertArrayHasKey('customerAddressUpdate', $response); + $this->assertArrayHasKey('customer_id', $response['customerAddressUpdate']); + $this->assertEquals($customer->getId(), $response['customerAddressUpdate']['customer_id']); + $this->assertArrayHasKey('id', $response['customerAddressUpdate']); /** @var AddressRepositoryInterface $addressRepository */ $addressRepository = ObjectManager::getInstance()->get(AddressRepositoryInterface::class); - $addressId = $response['customerAddressCreate']['id']; $address = $addressRepository->getById($addressId); - $this->assertEquals($address->getId(), $response['customerAddressCreate']['id']); - $this->assertCustomerAddressesFields($address, $response['customerAddressCreate']); - $this->assertCustomerAddressesFields($address, $newAddress); + $this->assertEquals($address->getId(), $response['customerAddressUpdate']['id']); + $this->assertCustomerAddressesFields($address, $response['customerAddressUpdate']); + $this->assertCustomerAddressesFields($address, $updateAddress); } /** - * Verify customers without credentials create new address + * Verify customers without credentials update address * * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testAddCustomerAddressWithoutCredentials() + public function testUpdateCustomerAddressWithoutCredentials() { + $userName = 'customer@example.com'; + /** @var CustomerRepositoryInterface $customerRepository */ + $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); + $customer = $customerRepository->get($userName); + /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ + $addresses = $customer->getAddresses(); + /** @var \Magento\Customer\Api\Data\AddressInterface $address */ + $address = current($addresses); + $addressId = $address->getId(); $mutation = <<expectException(\Exception::class); $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . - 'Current customer does not have access to the resource "customer"'); + 'Current customer does not have access to the resource "customer_address"'); $this->graphQlQuery($mutation); } /** - * Verify customers with valid credentials update address + * Verify customers with credentials update address + * with missing required Firstname attribute * * @magentoApiDataFixture Magento/Customer/_files/customer.php * @magentoApiDataFixture Magento/Customer/_files/customer_address.php * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testUpdateCustomerAddressWithValidCredentials() + public function testUpdateCustomerAddressWithMissingAttributeWithValidCredentials() { $userName = 'customer@example.com'; $password = 'password'; @@ -188,7 +195,7 @@ public function testUpdateCustomerAddressWithValidCredentials() 'fax' => '123123123', 'postcode' => '7777', 'city' => 'City Name', - 'firstname' => 'Adam', + 'firstname' => '', //empty name 'lastname' => 'Phillis', 'middlename' => 'A', 'prefix' => 'Mr.', @@ -258,219 +265,6 @@ public function testUpdateCustomerAddressWithValidCredentials() default_billing } } -MUTATION; - /** @var CustomerTokenServiceInterface $customerTokenService */ - $customerTokenService = ObjectManager::getInstance()->get(CustomerTokenServiceInterface::class); - $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); - $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - /** @var CustomerRepositoryInterface $customerRepository */ - $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); - $customer = $customerRepository->get($userName); - $response = $this->graphQlQuery($mutation, [], '', $headerMap); - $this->assertArrayHasKey('customerAddressUpdate', $response); - $this->assertArrayHasKey('customer_id', $response['customerAddressUpdate']); - $this->assertEquals($customer->getId(), $response['customerAddressUpdate']['customer_id']); - $this->assertArrayHasKey('id', $response['customerAddressUpdate']); - /** @var AddressRepositoryInterface $addressRepository */ - $addressRepository = ObjectManager::getInstance()->get(AddressRepositoryInterface::class); - $address = $addressRepository->getById($addressId); - $this->assertEquals($address->getId(), $response['customerAddressUpdate']['id']); - $this->assertCustomerAddressesFields($address, $response['customerAddressUpdate']); - $this->assertCustomerAddressesFields($address, $updateAddress); - } - - /** - * Verify customers without credentials update address - * - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Customer/_files/customer_address.php - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testUpdateCustomerAddressWithoutCredentials() - { - $userName = 'customer@example.com'; - /** @var CustomerRepositoryInterface $customerRepository */ - $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); - $customer = $customerRepository->get($userName); - /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ - $addresses = $customer->getAddresses(); - /** @var \Magento\Customer\Api\Data\AddressInterface $address */ - $address = current($addresses); - $addressId = $address->getId(); - $mutation - = <<expectException(\Exception::class); - $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . - 'Current customer does not have access to the resource "customer"'); - $this->graphQlQuery($mutation); - } - - /** - * Verify customers with valid credentials with a customer bearer token - * - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testDeleteCustomerAddressWithValidCredentials() - { - $userName = 'customer@example.com'; - $password = 'password'; - /** @var CustomerRepositoryInterface $customerRepository */ - $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); - $customer = $customerRepository->get($userName); - /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ - $addresses = $customer->getAddresses(); - /** @var \Magento\Customer\Api\Data\AddressInterface $address */ - $address = end($addresses); - $addressId = $address->getId(); - $mutation - = <<get(CustomerTokenServiceInterface::class); - $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); - $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - $response = $this->graphQlQuery($mutation, [], '', $headerMap); - $this->assertArrayHasKey('customerAddressDelete', $response); - $this->assertEquals(true, $response['customerAddressDelete']); - } - - /** - * Verify customers without credentials delete address - * - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Customer/_files/customer_address.php - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testDeleteCustomerAddressWithoutCredentials() - { - $userName = 'customer@example.com'; - /** @var CustomerRepositoryInterface $customerRepository */ - $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); - $customer = $customerRepository->get($userName); - /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ - $addresses = $customer->getAddresses(); - /** @var \Magento\Customer\Api\Data\AddressInterface $address */ - $address = current($addresses); - $addressId = $address->getId(); - $mutation - = <<expectException(\Exception::class); - $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . - 'Current customer does not have access to the resource "customer"'); - $this->graphQlQuery($mutation); - } - - /** - * Verify customers with valid credentials delete default shipping address - * - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testDeleteDefaultShippingCustomerAddressWithValidCredentials() - { - $userName = 'customer@example.com'; - $password = 'password'; - /** @var CustomerRepositoryInterface $customerRepository */ - $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); - $customer = $customerRepository->get($userName); - /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ - $addresses = $customer->getAddresses(); - /** @var \Magento\Customer\Api\Data\AddressInterface $address */ - $address = end($addresses); - $address->setIsDefaultShipping(true); - $addressRepository = ObjectManager::getInstance()->get(AddressRepositoryInterface::class); - $addressRepository->save($address); - $addressId = $address->getId(); - $mutation - = <<get(CustomerTokenServiceInterface::class); - $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); - $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - $this->expectException(\Exception::class); - $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . - 'Customer Address ' . $addressId . ' is set as default shipping address and can not be deleted'); - $this->graphQlQuery($mutation, [], '', $headerMap); - } - - /** - * Verify customers with valid credentials delete default billing address - * - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @magentoApiDataFixture Magento/Customer/_files/customer_two_addresses.php - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testDeleteDefaultBillingCustomerAddressWithValidCredentials() - { - $userName = 'customer@example.com'; - $password = 'password'; - /** @var CustomerRepositoryInterface $customerRepository */ - $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); - $customer = $customerRepository->get($userName); - /** @var \Magento\Customer\Api\Data\AddressInterface[] $addresses */ - $addresses = $customer->getAddresses(); - /** @var \Magento\Customer\Api\Data\AddressInterface $address */ - $address = end($addresses); - $address->setIsDefaultBilling(true); - $addressRepository = ObjectManager::getInstance()->get(AddressRepositoryInterface::class); - $addressRepository->save($address); - $addressId = $address->getId(); - $mutation - = <<get(CustomerTokenServiceInterface::class); - $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password); - $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; - $this->expectException(\Exception::class); - $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . - 'Customer Address ' . $addressId . ' is set as default billing address and can not be deleted'); - $this->graphQlQuery($mutation, [], '', $headerMap); - } - - /** - * Verify customers with valid credentials delete non exist address - * - * @magentoApiDataFixture Magento/Customer/_files/customer.php - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function testDeleteNonExistCustomerAddressWithValidCredentials() - { - $userName = 'customer@example.com'; - $password = 'password'; - $mutation - = <<get(CustomerTokenServiceInterface::class); @@ -478,7 +272,7 @@ public function testDeleteNonExistCustomerAddressWithValidCredentials() $headerMap = ['Authorization' => 'Bearer ' . $customerToken]; $this->expectException(\Exception::class); $this->expectExceptionMessage('GraphQL response contains errors:' . ' ' . - 'Address id 9999 does not exist.'); + 'Required parameter firstname is missing'); $this->graphQlQuery($mutation, [], '', $headerMap); } From 6c6fdbcc1f959b0dfe2b85f1f814ce2590306514 Mon Sep 17 00:00:00 2001 From: Pablo Fantini Date: Mon, 1 Oct 2018 21:43:03 -0300 Subject: [PATCH 030/704] GraphQL-57: Improve doc --- .../Model/Resolver/Address/AddressConfigProvider.php | 2 +- .../Magento/CustomerGraphQl/Model/Resolver/AddressCreate.php | 2 +- .../Magento/CustomerGraphQl/Model/Resolver/AddressDelete.php | 2 +- .../Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressConfigProvider.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressConfigProvider.php index 8f16d2890f5e1..363071b7b860d 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressConfigProvider.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressConfigProvider.php @@ -13,7 +13,7 @@ use Magento\Eav\Model\Config; /** - * Customers Address, used for GraphQL request processing. + * Customers address configuration, used for GraphQL request processing. */ class AddressConfigProvider { diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressCreate.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressCreate.php index 0420a9d91049a..1737ae8018f0a 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressCreate.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressCreate.php @@ -22,7 +22,7 @@ use Magento\CustomerGraphQl\Model\Resolver\Address\AddressConfigProvider; /** - * Customers Address, used for GraphQL request processing. + * Customers address create, used for GraphQL request processing. */ class AddressCreate implements ResolverInterface { diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressDelete.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressDelete.php index 540d7645cc657..e636f3eceeed8 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressDelete.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressDelete.php @@ -20,7 +20,7 @@ use Magento\Framework\Exception\NoSuchEntityException; /** - * Customers Address, used for GraphQL request processing. + * Customers address delete, used for GraphQL request processing. */ class AddressDelete implements ResolverInterface { diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php index 800ba5f2804f4..f0841adcf8127 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php @@ -24,7 +24,7 @@ use Magento\CustomerGraphQl\Model\Resolver\Address\AddressConfigProvider; /** - * Customers Address, used for GraphQL request processing. + * Customers address update, used for GraphQL request processing. */ class AddressUpdate implements ResolverInterface { From 1b085c963f49d6e11319ada0f3ed0533728fbad9 Mon Sep 17 00:00:00 2001 From: vprohorov Date: Tue, 2 Oct 2018 15:08:19 +0300 Subject: [PATCH 031/704] MAGETWO-94444: [2.3] Order total value is limited by 8 round digits - Changing precision in database schema --- app/code/Magento/Quote/etc/db_schema.xml | 36 +++--- app/code/Magento/Sales/etc/db_schema.xml | 146 +++++++++++------------ 2 files changed, 91 insertions(+), 91 deletions(-) diff --git a/app/code/Magento/Quote/etc/db_schema.xml b/app/code/Magento/Quote/etc/db_schema.xml index 725825e976ad7..b222fdad40598 100644 --- a/app/code/Magento/Quote/etc/db_schema.xml +++ b/app/code/Magento/Quote/etc/db_schema.xml @@ -37,9 +37,9 @@ comment="Store Currency Code"/> - - - - - - - - @@ -143,13 +143,13 @@ comment="Shipping Description"/> - - - - @@ -167,9 +167,9 @@ default="0" comment="Discount Amount"/> - - @@ -179,9 +179,9 @@ nullable="true" comment="Shipping Discount Amount"/> - - @@ -372,9 +372,9 @@ comment="Base Cost"/> - - diff --git a/app/code/Magento/Sales/etc/db_schema.xml b/app/code/Magento/Sales/etc/db_schema.xml index fda6e75cf00c6..d09c5daa12edd 100644 --- a/app/code/Magento/Sales/etc/db_schema.xml +++ b/app/code/Magento/Sales/etc/db_schema.xml @@ -30,7 +30,7 @@ nullable="true" comment="Base Discount Invoiced"/> - @@ -44,13 +44,13 @@ nullable="true" comment="Base Shipping Tax Amount"/> - - - - @@ -62,23 +62,23 @@ comment="Base Tax Refunded"/> - - - - - - - - @@ -88,7 +88,7 @@ comment="Discount Invoiced"/> - @@ -106,13 +106,13 @@ comment="Store To Base Rate"/> - - - - @@ -126,15 +126,15 @@ comment="Total Canceled"/> - - - - @@ -173,17 +173,17 @@ nullable="true" comment="Base Adjustment Positive"/> - - - - - @@ -304,13 +304,13 @@ - - - - @@ -326,7 +326,7 @@ comment="Shipping Method Name"/> - @@ -929,7 +929,7 @@ comment="Entity ID"/> - @@ -945,15 +945,15 @@ nullable="true" comment="Base Discount Amount"/> - - - - @@ -961,9 +961,9 @@ comment="Total Qty"/> - - @@ -1006,7 +1006,7 @@ comment="Shipping Incl Tax"/> - @@ -1077,15 +1077,15 @@ - - - @@ -1143,11 +1143,11 @@ comment="Base Price"/> - - @@ -1230,15 +1230,15 @@ nullable="true" comment="Base Discount Amount"/> - - - @@ -1250,15 +1250,15 @@ comment="Base To Global Rate"/> - - - @@ -1362,7 +1362,7 @@ - - @@ -1593,19 +1593,19 @@ default="0" comment="Total Qty Ordered"/> - - - - - - - @@ -1647,19 +1647,19 @@ default="0" comment="Total Qty Ordered"/> - - - - - - - @@ -1737,11 +1737,11 @@ - - - @@ -1767,11 +1767,11 @@ - - - From 6436bcb6100394faa8519a709199de550e6fbc7b Mon Sep 17 00:00:00 2001 From: Pablo Fantini Date: Tue, 2 Oct 2018 10:09:19 -0300 Subject: [PATCH 032/704] GraphQL-57: Remove unused dependencies --- .../Magento/CustomerGraphQl/Model/Resolver/AddressCreate.php | 1 - .../Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php | 1 - 2 files changed, 2 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressCreate.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressCreate.php index 1737ae8018f0a..f336d8d73e567 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressCreate.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressCreate.php @@ -8,7 +8,6 @@ namespace Magento\CustomerGraphQl\Model\Resolver; use Magento\Authorization\Model\UserContextInterface; -use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\AddressMetadataManagementInterface; use Magento\Customer\Api\Data\AddressInterfaceFactory; diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php index f0841adcf8127..5a77ca8091104 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php @@ -8,7 +8,6 @@ namespace Magento\CustomerGraphQl\Model\Resolver; use Magento\Authorization\Model\UserContextInterface; -use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\AddressMetadataManagementInterface; use Magento\Customer\Api\Data\AddressInterfaceFactory; From bfd4fc10b58326a8201b7ba051fc7e1fbb5254ce Mon Sep 17 00:00:00 2001 From: Pablo Fantini Date: Tue, 2 Oct 2018 16:27:39 -0300 Subject: [PATCH 033/704] GraphQL-57: Refactor address update and data provider --- .../Resolver/Address/AddressDataProvider.php | 37 ++++++++++-- .../Model/Resolver/AddressUpdate.php | 9 --- .../Customer/CustomerAddressCreateTest.php | 58 ++++--------------- .../Customer/CustomerAddressUpdateTest.php | 48 +-------------- 4 files changed, 45 insertions(+), 107 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php index b794c4ef58794..5f93ad82a3760 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php @@ -8,7 +8,11 @@ namespace Magento\CustomerGraphQl\Model\Resolver\Address; use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Framework\Api\CustomAttributesDataInterface; use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Model\ResourceModel\Customer as CustomerResourceModel; +use Magento\Customer\Model\CustomerFactory; use Magento\Framework\Webapi\ServiceOutputProcessor; use Magento\Framework\Serialize\SerializerInterface; @@ -27,16 +31,32 @@ class AddressDataProvider */ private $jsonSerializer; + /** + * @var CustomerResourceModel + */ + private $customerResourceModel; + + /** + * @var CustomerFactory + */ + private $customerFactory; + /** * @param ServiceOutputProcessor $serviceOutputProcessor * @param SerializerInterface $jsonSerializer + * @param CustomerResourceModel $customerResourceModel + * @param CustomerFactory $customerFactory */ public function __construct( ServiceOutputProcessor $serviceOutputProcessor, - SerializerInterface $jsonSerializer + SerializerInterface $jsonSerializer, + CustomerResourceModel $customerResourceModel, + CustomerFactory $customerFactory ) { $this->serviceOutputProcessor = $serviceOutputProcessor; $this->jsonSerializer = $jsonSerializer; + $this->customerResourceModel = $customerResourceModel; + $this->customerFactory = $customerFactory; } /** @@ -52,12 +72,19 @@ public function processCustomerAddress(AddressInterface $addressObject) : array AddressRepositoryInterface::class, 'getById' ); - if (isset($address['extension_attributes'])) { - $address = array_merge($address, $address['extension_attributes']); + $customerModel = $this->customerFactory->create(); + $this->customerResourceModel->load($customerModel, $addressObject->getCustomerId()); + $address[CustomerInterface::DEFAULT_BILLING] = + ($addressObject->getId() == $customerModel->getDefaultBillingAddress()->getId()) ? true : false; + $address[CustomerInterface::DEFAULT_SHIPPING] = + ($addressObject->getId() == $customerModel->getDefaultShippingAddress()->getId()) ? true : false; + + if (isset($address[CustomAttributesDataInterface::EXTENSION_ATTRIBUTES_KEY])) { + $address = array_merge($address, $address[CustomAttributesDataInterface::EXTENSION_ATTRIBUTES_KEY]); } $customAttributes = []; - if (isset($address['custom_attributes'])) { - foreach ($address['custom_attributes'] as $attribute) { + if (isset($address[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES])) { + foreach ($address[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES] as $attribute) { $isArray = false; if (is_array($attribute['value'])) { $isArray = true; diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php index 5a77ca8091104..63111138e3d73 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/AddressUpdate.php @@ -10,7 +10,6 @@ use Magento\Authorization\Model\UserContextInterface; use Magento\Customer\Api\AddressRepositoryInterface; use Magento\Customer\Api\AddressMetadataManagementInterface; -use Magento\Customer\Api\Data\AddressInterfaceFactory; use Magento\Customer\Api\Data\AddressInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; @@ -32,11 +31,6 @@ class AddressUpdate implements ResolverInterface */ private $addressRepositoryInterface; - /** - * @var AddressInterfaceFactory - */ - private $addressInterfaceFactory; - /** * @var AddressDataProvider */ @@ -49,18 +43,15 @@ class AddressUpdate implements ResolverInterface /** * @param AddressRepositoryInterface $addressRepositoryInterface - * @param AddressInterfaceFactory $addressInterfaceFactory * @param AddressDataProvider $addressDataProvider * @param AddressConfigProvider $addressConfigProvider */ public function __construct( AddressRepositoryInterface $addressRepositoryInterface, - AddressInterfaceFactory $addressInterfaceFactory, AddressDataProvider $addressDataProvider, AddressConfigProvider $addressConfigProvider ) { $this->addressRepositoryInterface = $addressRepositoryInterface; - $this->addressInterfaceFactory = $addressInterfaceFactory; $this->addressDataProvider = $addressDataProvider; $this->addressConfigProvider = $addressConfigProvider; } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressCreateTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressCreateTest.php index 2db574c508827..4e2d248b97eb9 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressCreateTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/CustomerAddressCreateTest.php @@ -45,7 +45,7 @@ public function testAddCustomerAddressWithValidCredentials() 'prefix' => 'Mr.', 'suffix' => 'Jr.', 'vat_id' => '1', - 'default_shipping' => false, + 'default_shipping' => true, 'default_billing' => false ]; $defaultShippingText = $newAddress['default_shipping'] ? "true": "false"; @@ -172,56 +172,20 @@ public function testAddCustomerAddressWithoutCredentials() */ public function testAddCustomerAddressWithMissingAttributeWithValidCredentials() { - $newAddress = [ - 'region' => [ - 'region' => 'Alaska', - 'region_id' => 4, - 'region_code' => 'AK' - ], - 'region_id' => 4, - 'country_id' => 'US', - 'street' => ['Line 1 Street', 'Line 2'], - 'company' => 'Company name', - 'telephone' => '123456789', - 'fax' => '123123123', - 'postcode' => '7777', - 'city' => 'City Name', - 'firstname' => '', //empty firstname - 'lastname' => 'Phillis', - 'middlename' => 'A', - 'prefix' => 'Mr.', - 'suffix' => 'Jr.', - 'vat_id' => '1', - 'default_shipping' => false, - 'default_billing' => false - ]; - $defaultShippingText = $newAddress['default_shipping'] ? "true": "false"; - $defaultBillingText = $newAddress['default_billing'] ? "true": "false"; $mutation = << [ - 'region' => 'Alaska', - 'region_id' => 4, - 'region_code' => 'AK' - ], - 'region_id' => 4, - 'country_id' => 'US', - 'street' => ['Line 1 Street', 'Line 2'], - 'company' => 'Company Name', - 'telephone' => '123456789', - 'fax' => '123123123', - 'postcode' => '7777', - 'city' => 'City Name', - 'firstname' => '', //empty name - 'lastname' => 'Phillis', - 'middlename' => 'A', - 'prefix' => 'Mr.', - 'suffix' => 'Jr.', - 'vat_id' => '1', - 'default_shipping' => true, - 'default_billing' => true - ]; - $defaultShippingText = $updateAddress['default_shipping'] ? "true": "false"; - $defaultBillingText = $updateAddress['default_billing'] ? "true": "false"; /** @var CustomerRepositoryInterface $customerRepository */ $customerRepository = ObjectManager::getInstance()->get(CustomerRepositoryInterface::class); $customer = $customerRepository->get($userName); @@ -218,27 +193,8 @@ public function testUpdateCustomerAddressWithMissingAttributeWithValidCredential = << Date: Tue, 2 Oct 2018 18:03:35 -0300 Subject: [PATCH 034/704] GraphQL-57: Fix cyclomatic complexity in addressDataProvider --- .../Resolver/Address/AddressDataProvider.php | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php index 5f93ad82a3760..2b0cbe036ada7 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php @@ -59,6 +59,24 @@ public function __construct( $this->customerFactory = $customerFactory; } + /** + * Curate shipping and billing default options + * + * @param array $address + * @param AddressInterface $addressObject + * @return null + */ + private function curateAddressDefaultValues(array $address, AddressInterface $addressObject) : array + { + $customerModel = $this->customerFactory->create(); + $this->customerResourceModel->load($customerModel, $addressObject->getCustomerId()); + $address[CustomerInterface::DEFAULT_BILLING] = + ($addressObject->getId() == $customerModel->getDefaultBillingAddress()->getId()) ? true : false; + $address[CustomerInterface::DEFAULT_SHIPPING] = + ($addressObject->getId() == $customerModel->getDefaultShippingAddress()->getId()) ? true : false; + return $address; + } + /** * Transform single customer address data from object to in array format * @@ -72,12 +90,7 @@ public function processCustomerAddress(AddressInterface $addressObject) : array AddressRepositoryInterface::class, 'getById' ); - $customerModel = $this->customerFactory->create(); - $this->customerResourceModel->load($customerModel, $addressObject->getCustomerId()); - $address[CustomerInterface::DEFAULT_BILLING] = - ($addressObject->getId() == $customerModel->getDefaultBillingAddress()->getId()) ? true : false; - $address[CustomerInterface::DEFAULT_SHIPPING] = - ($addressObject->getId() == $customerModel->getDefaultShippingAddress()->getId()) ? true : false; + $address = $this->curateAddressDefaultValues($address, $addressObject); if (isset($address[CustomAttributesDataInterface::EXTENSION_ATTRIBUTES_KEY])) { $address = array_merge($address, $address[CustomAttributesDataInterface::EXTENSION_ATTRIBUTES_KEY]); From 4927e565cc54f9395ba8ef4bcf3d945cd3020052 Mon Sep 17 00:00:00 2001 From: Danny Verkade Date: Wed, 3 Oct 2018 10:29:15 +0200 Subject: [PATCH 035/704] Fixed coding style errors. --- .../Magento/Catalog/Model/Product/Option/Validator/Select.php | 3 +++ .../testsuite/Magento/Catalog/Api/_files/product_options.php | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php b/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php index af37c16c4053e..209531f599811 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php @@ -8,6 +8,9 @@ use Magento\Catalog\Model\Product\Option; +/** + * Select validator class + */ class Select extends DefaultValidator { /** diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php index e61e6061f4fc2..8a00de1be094f 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/_files/product_options.php @@ -153,4 +153,4 @@ 'price_type' => 'fixed', 'sku' => 'time option sku', ], -]; \ No newline at end of file +]; From 7400261ffcb052625070cf79a2d75f60282590cd Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Wed, 3 Oct 2018 13:17:04 +0300 Subject: [PATCH 036/704] MAGETWO-93769: Catalog performance improvements --- .../Model/Product/Price/TierPriceStorage.php | 21 ++++++++++--------- .../Catalog/Model/ProductIdLocator.php | 15 +++++++++++-- .../ResourceModel/Product/Collection.php | 10 ++++++++- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php index 1bddd2d07cd81..7298e650bd448 100644 --- a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php @@ -171,16 +171,17 @@ private function getExistingPrices(array $skus, $groupBySku = false) $ids = $this->retrieveAffectedIds($skus); $rawPrices = $this->tierPricePersistence->get($ids); $prices = []; - - $linkField = $this->tierPricePersistence->getEntityLinkField(); - $skuByIdLookup = $this->buildSkuByIdLookup($skus); - foreach ($rawPrices as $rawPrice) { - $sku = $skuByIdLookup[$rawPrice[$linkField]]; - $price = $this->tierPriceFactory->create($rawPrice, $sku); - if ($groupBySku) { - $prices[$sku][] = $price; - } else { - $prices[] = $price; + if ($rawPrices) { + $linkField = $this->tierPricePersistence->getEntityLinkField(); + $skuByIdLookup = $this->buildSkuByIdLookup($skus); + foreach ($rawPrices as $rawPrice) { + $sku = $skuByIdLookup[$rawPrice[$linkField]]; + $price = $this->tierPriceFactory->create($rawPrice, $sku); + if ($groupBySku) { + $prices[$sku][] = $price; + } else { + $prices[] = $price; + } } } diff --git a/app/code/Magento/Catalog/Model/ProductIdLocator.php b/app/code/Magento/Catalog/Model/ProductIdLocator.php index 2d9af6829ad6e..ba8fa01b40f58 100644 --- a/app/code/Magento/Catalog/Model/ProductIdLocator.php +++ b/app/code/Magento/Catalog/Model/ProductIdLocator.php @@ -37,6 +37,11 @@ class ProductIdLocator implements \Magento\Catalog\Model\ProductIdLocatorInterfa */ private $idsBySku = []; + /** + * Page size to iterate collection + */ + private $pageSize = 10000; + /** * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory @@ -72,8 +77,14 @@ public function retrieveProductIdsBySkus(array $skus) $linkField = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class) ->getLinkField(); - foreach ($collection as $item) { - $this->idsBySku[strtolower(trim($item->getSku()))][$item->getData($linkField)] = $item->getTypeId(); + $collection->setPageSize($this->pageSize); + $pages = $collection->getLastPageNumber(); + for ($currentPage = 1; $currentPage <= $pages; $currentPage++) { + $collection->setCurPage($currentPage); + foreach ($collection->getItems() as $item) { + $this->idsBySku[strtolower(trim($item->getSku()))][$item->getData($linkField)] = $item->getTypeId(); + } + $collection->clear(); } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index fdd98442150ae..4aa427223dcbe 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -290,6 +290,11 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac */ private $dimensionFactory; + /** + * @var \Magento\Framework\DataObject + */ + private $emptyItem; + /** * Collection constructor * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory @@ -550,7 +555,10 @@ protected function _prepareStaticFields() */ public function getNewEmptyItem() { - $object = parent::getNewEmptyItem(); + if (null === $this->emptyItem) { + $this->emptyItem = parent::getNewEmptyItem(); + } + $object = clone $this->emptyItem; if ($this->isEnabledFlat()) { $object->setIdFieldName($this->getEntity()->getIdFieldName()); } From 6c7ce718ea5d2cc91269242963bda2293388f00e Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Thu, 4 Oct 2018 11:56:26 +0300 Subject: [PATCH 037/704] MAGETWO-93769: Catalog performance improvements --- .../Catalog/Model/ProductIdLocator.php | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ProductIdLocator.php b/app/code/Magento/Catalog/Model/ProductIdLocator.php index ba8fa01b40f58..21ddf06427982 100644 --- a/app/code/Magento/Catalog/Model/ProductIdLocator.php +++ b/app/code/Magento/Catalog/Model/ProductIdLocator.php @@ -38,23 +38,28 @@ class ProductIdLocator implements \Magento\Catalog\Model\ProductIdLocatorInterfa private $idsBySku = []; /** - * Page size to iterate collection + * Batch size to iterate collection + * + * @var int */ - private $pageSize = 10000; + private $batchSize; /** * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory - * @param string $limitIdsBySkuValues + * @param string $idsLimit + * @param int $batchSize [optional] */ public function __construct( \Magento\Framework\EntityManager\MetadataPool $metadataPool, \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory, - $idsLimit + $idsLimit, + $batchSize = 5000 ) { $this->metadataPool = $metadataPool; $this->collectionFactory = $collectionFactory; $this->idsLimit = (int)$idsLimit; + $this->batchSize = $batchSize; } /** @@ -77,12 +82,14 @@ public function retrieveProductIdsBySkus(array $skus) $linkField = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class) ->getLinkField(); - $collection->setPageSize($this->pageSize); + $collection->setPageSize($this->batchSize); $pages = $collection->getLastPageNumber(); for ($currentPage = 1; $currentPage <= $pages; $currentPage++) { $collection->setCurPage($currentPage); foreach ($collection->getItems() as $item) { - $this->idsBySku[strtolower(trim($item->getSku()))][$item->getData($linkField)] = $item->getTypeId(); + $sku = strtolower(trim($item->getSku())); + $itemIdentifier = $item->getData($linkField); + $this->idsBySku[$sku][$itemIdentifier] = $item->getTypeId(); } $collection->clear(); } From bdf8c0d5632bf05ce6be5a6f084dba46918a2297 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Thu, 4 Oct 2018 14:22:38 +0300 Subject: [PATCH 038/704] MAGETWO-93769: Catalog performance improvements --- app/code/Magento/Catalog/Model/ProductIdLocator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ProductIdLocator.php b/app/code/Magento/Catalog/Model/ProductIdLocator.php index 21ddf06427982..e51deb7f6cbe7 100644 --- a/app/code/Magento/Catalog/Model/ProductIdLocator.php +++ b/app/code/Magento/Catalog/Model/ProductIdLocator.php @@ -48,13 +48,13 @@ class ProductIdLocator implements \Magento\Catalog\Model\ProductIdLocatorInterfa * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory * @param string $idsLimit - * @param int $batchSize [optional] + * @param int $batchSize defines how many items can be processed by one request */ public function __construct( \Magento\Framework\EntityManager\MetadataPool $metadataPool, \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory, $idsLimit, - $batchSize = 5000 + int $batchSize = 5000 ) { $this->metadataPool = $metadataPool; $this->collectionFactory = $collectionFactory; From b0bc510b885067a725ecce2339f614327528797d Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Thu, 4 Oct 2018 17:21:05 +0300 Subject: [PATCH 039/704] MAGETWO-93769: Catalog performance improvements --- .../Catalog/Model/ProductIdLocator.php | 4 +-- .../Product/Price/TierPriceStorageTest.php | 24 ++++++++++++++++++ .../Test/Unit/Model/ProductIdLocatorTest.php | 17 +++++++++++-- .../ResourceModel/Product/CollectionTest.php | 25 +++++++++++++++++-- 4 files changed, 64 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ProductIdLocator.php b/app/code/Magento/Catalog/Model/ProductIdLocator.php index e51deb7f6cbe7..4c4e6b416527b 100644 --- a/app/code/Magento/Catalog/Model/ProductIdLocator.php +++ b/app/code/Magento/Catalog/Model/ProductIdLocator.php @@ -48,7 +48,7 @@ class ProductIdLocator implements \Magento\Catalog\Model\ProductIdLocatorInterfa * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory * @param string $idsLimit - * @param int $batchSize defines how many items can be processed by one request + * @param int $batchSize defines how many items can be processed by one iteration */ public function __construct( \Magento\Framework\EntityManager\MetadataPool $metadataPool, @@ -86,7 +86,7 @@ public function retrieveProductIdsBySkus(array $skus) $pages = $collection->getLastPageNumber(); for ($currentPage = 1; $currentPage <= $pages; $currentPage++) { $collection->setCurPage($currentPage); - foreach ($collection->getItems() as $item) { + foreach ($collection->getIterator() as $item) { $sku = strtolower(trim($item->getSku())); $itemIdentifier = $item->getData($linkField); $this->idsBySku[$sku][$itemIdentifier] = $item->getTypeId(); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php index c9288790ed6e1..a97f2281125a6 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php @@ -152,6 +152,30 @@ public function testGet() $this->assertEquals(2, count($prices)); } + /** + * Test get method without tierprices. + * + * @return void + */ + public function testGetWithoutTierPrices() + { + $skus = ['simple', 'virtual']; + $this->tierPriceValidator + ->expects($this->once()) + ->method('validateSkus') + ->with($skus) + ->willReturn($skus); + $this->productIdLocator->expects($this->atLeastOnce()) + ->method('retrieveProductIdsBySkus') + ->with(['simple', 'virtual']) + ->willReturn(['simple' => ['2' => 'simple'], 'virtual' => ['3' => 'virtual']]); + $this->tierPricePersistence->expects($this->once())->method('get')->willReturn([]); + $this->tierPricePersistence->expects($this->never())->method('getEntityLinkField'); + $this->tierPriceFactory->expects($this->never())->method('create'); + $prices = $this->tierPriceStorage->get($skus); + $this->assertEmpty($prices); + } + /** * Test update method. * diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php index b730e12ca820b..6455aa58dd234 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php @@ -58,7 +58,16 @@ public function testRetrieveProductIdsBySkus() { $skus = ['sku_1', 'sku_2']; $collection = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\Collection::class) - ->setMethods(['getIterator', 'addFieldToFilter']) + ->setMethods( + [ + 'getIterator', + 'addFieldToFilter', + 'setPageSize', + 'getLastPageNumber', + 'setCurPage', + 'clear' + ] + ) ->disableOriginalConstructor()->getMock(); $product = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) ->setMethods(['getSku', 'getData', 'getTypeId']) @@ -69,7 +78,11 @@ public function testRetrieveProductIdsBySkus() $this->collectionFactory->expects($this->once())->method('create')->willReturn($collection); $collection->expects($this->once())->method('addFieldToFilter') ->with(\Magento\Catalog\Api\Data\ProductInterface::SKU, ['in' => $skus])->willReturnSelf(); - $collection->expects($this->once())->method('getIterator')->willReturn(new \ArrayIterator([$product])); + $collection->expects($this->atLeastOnce())->method('getIterator')->willReturn(new \ArrayIterator([$product])); + $collection->expects($this->atLeastOnce())->method('setPageSize')->willReturnSelf(); + $collection->expects($this->atLeastOnce())->method('getLastPageNumber')->willReturn(1); + $collection->expects($this->atLeastOnce())->method('setCurPage')->with(1)->willReturnSelf(); + $collection->expects($this->atLeastOnce())->method('clear')->willReturnSelf(); $this->metadataPool ->expects($this->once()) ->method('getMetadata') diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php index dbbb3fb29513b..bb39aa7f9db77 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CollectionTest.php @@ -58,13 +58,18 @@ class CollectionTest extends \PHPUnit\Framework\TestCase */ private $storeManager; + /** + * @var \Magento\Framework\Data\Collection\EntityFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $entityFactory; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $entityFactory = $this->createMock(\Magento\Framework\Data\Collection\EntityFactory::class); + $this->entityFactory = $this->createMock(\Magento\Framework\Data\Collection\EntityFactory::class); $logger = $this->getMockBuilder(\Psr\Log\LoggerInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); @@ -168,7 +173,7 @@ protected function setUp() $this->collection = $this->objectManager->getObject( \Magento\Catalog\Model\ResourceModel\Product\Collection::class, [ - 'entityFactory' => $entityFactory, + 'entityFactory' => $this->entityFactory, 'logger' => $logger, 'fetchStrategy' => $fetchStrategy, 'eventManager' => $eventManager, @@ -379,4 +384,20 @@ public function testAddTierPriceData() $this->assertSame($this->collection, $this->collection->addTierPriceData()); } + + /** + * Test for getNewEmptyItem() method + * + * @return void + */ + public function testGetNewEmptyItem() + { + $item = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + ->disableOriginalConstructor() + ->getMock(); + $this->entityFactory->expects($this->once())->method('create')->willReturn($item); + $firstItem = $this->collection->getNewEmptyItem(); + $secondItem = $this->collection->getNewEmptyItem(); + $this->assertEquals($firstItem, $secondItem); + } } From 9dc3345ae68c6a90d1a19d58d0ec29bc07d15198 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi Date: Thu, 4 Oct 2018 22:20:21 +0300 Subject: [PATCH 040/704] Fix pr --- .../DataProvider/NotificationDataProvider.php | 84 ++-------- .../Magento/Sales/Model/Order/Address.php | 153 +++--------------- .../Sales/Model/Order/Payment/Transaction.php | 39 +---- app/code/Magento/Store/Model/Store.php | 9 +- 4 files changed, 50 insertions(+), 235 deletions(-) diff --git a/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php b/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php index 26e698b67545b..8418b4f9a753d 100644 --- a/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php +++ b/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php @@ -82,9 +82,7 @@ public function __construct( } /** - * Get data - * - * @return mixed + * @inheritdoc */ public function getData() { @@ -97,10 +95,7 @@ public function getData() } /** - * Get Meta - * - * @return array - * @throws \Magento\Framework\Exception\LocalizedException + * @inheritdoc */ public function getMeta() { @@ -112,9 +107,7 @@ public function getMeta() } /** - * Get Data Provider name - * - * @return string + * @inheritdoc */ public function getName() { @@ -122,9 +115,7 @@ public function getName() } /** - * Get config data - * - * @return mixed + * @inheritdoc */ public function getConfigData() { @@ -132,11 +123,7 @@ public function getConfigData() } /** - * Set config data - * - * @param mixed $config - * - * @return bool + * @inheritdoc */ public function setConfigData($config) { @@ -146,13 +133,7 @@ public function setConfigData($config) } /** - * Get Field Meta Info - * - * @param string $fieldSetName - * @param string $fieldName - * - * @return array - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @inheritdoc */ public function getFieldMetaInfo($fieldSetName, $fieldName) { @@ -160,12 +141,7 @@ public function getFieldMetaInfo($fieldSetName, $fieldName) } /** - * Get Field Set Meta Info - * - * @param string $fieldSetName - * - * @return array - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @inheritdoc */ public function getFieldSetMetaInfo($fieldSetName) { @@ -173,12 +149,7 @@ public function getFieldSetMetaInfo($fieldSetName) } /** - * Get Fields Meta Info - * - * @param string $fieldSetName - * - * @return array - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @inheritdoc */ public function getFieldsMetaInfo($fieldSetName) { @@ -186,9 +157,7 @@ public function getFieldsMetaInfo($fieldSetName) } /** - * Get Primary Field Name - * - * @return string + * @inheritdoc */ public function getPrimaryFieldName() { @@ -196,9 +165,7 @@ public function getPrimaryFieldName() } /** - * Get Request Field Name - * - * @return string + * @inheritdoc */ public function getRequestFieldName() { @@ -206,47 +173,28 @@ public function getRequestFieldName() } /** - * Add Filter - * - * @param \Magento\Framework\Api\Filter $filter - * - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @inheritdoc */ public function addFilter(\Magento\Framework\Api\Filter $filter) { } /** - * Add Order - * - * @param string $field - * @param string $direction - * - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @inheritdoc */ public function addOrder($field, $direction) { } /** - * Set Limit - * - * @param int $offset - * @param int $size - * - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @inheritdoc */ public function setLimit($offset, $size) { } /** - * Get Search Criteria - * - * @return SearchCriteriaInterface + * @inheritdoc */ public function getSearchCriteria() { @@ -254,9 +202,7 @@ public function getSearchCriteria() } /** - * Get Search Result - * - * @return SearchResultInterface + * @inheritdoc */ public function getSearchResult() { diff --git a/app/code/Magento/Sales/Model/Order/Address.php b/app/code/Magento/Sales/Model/Order/Address.php index 98433899cca26..a19c587216b1c 100644 --- a/app/code/Magento/Sales/Model/Order/Address.php +++ b/app/code/Magento/Sales/Model/Order/Address.php @@ -516,11 +516,7 @@ public function setParentId($id) } /** - * Sets the country address ID for the order address. - * - * @param int $id - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setCustomerAddressId($id) { @@ -528,11 +524,7 @@ public function setCustomerAddressId($id) } /** - * Sets the region ID for the order address. - * - * @param int $id - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setRegionId($id) { @@ -540,11 +532,7 @@ public function setRegionId($id) } /** - * Sets the street values, if any, for the order address. - * - * @param string|string[] $street - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setStreet($street) { @@ -552,11 +540,7 @@ public function setStreet($street) } /** - * Sets the customer ID for the order address. - * - * @param int $id - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setCustomerId($id) { @@ -564,11 +548,7 @@ public function setCustomerId($id) } /** - * Sets the fax number for the order address. - * - * @param string $fax - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setFax($fax) { @@ -576,11 +556,7 @@ public function setFax($fax) } /** - * Sets the region for the order address. - * - * @param string $region - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setRegion($region) { @@ -588,11 +564,7 @@ public function setRegion($region) } /** - * Sets the postal code for the order address. - * - * @param string $postcode - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setPostcode($postcode) { @@ -600,11 +572,7 @@ public function setPostcode($postcode) } /** - * Sets the last name for the order address. - * - * @param string $lastname - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setLastname($lastname) { @@ -612,11 +580,7 @@ public function setLastname($lastname) } /** - * Sets the city for the order address. - * - * @param string $city - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setCity($city) { @@ -624,11 +588,7 @@ public function setCity($city) } /** - * Sets the email address for the order address. - * - * @param string $email - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setEmail($email) { @@ -636,11 +596,7 @@ public function setEmail($email) } /** - * Sets the telephone number for the order address. - * - * @param string $telephone - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setTelephone($telephone) { @@ -648,11 +604,7 @@ public function setTelephone($telephone) } /** - * Sets the country ID for the order address. - * - * @param string $id - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setCountryId($id) { @@ -660,11 +612,7 @@ public function setCountryId($id) } /** - * Sets the first name for the order address. - * - * @param string $firstname - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setFirstname($firstname) { @@ -672,11 +620,7 @@ public function setFirstname($firstname) } /** - * Sets the address type for the order address. - * - * @param string $addressType - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setAddressType($addressType) { @@ -684,11 +628,7 @@ public function setAddressType($addressType) } /** - * Sets the prefix for the order address. - * - * @param string $prefix - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setPrefix($prefix) { @@ -696,11 +636,7 @@ public function setPrefix($prefix) } /** - * Sets the middle name for the order address. - * - * @param string $middlename - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setMiddlename($middlename) { @@ -708,11 +644,7 @@ public function setMiddlename($middlename) } /** - * Sets the suffix for the order address. - * - * @param string $suffix - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setSuffix($suffix) { @@ -720,11 +652,7 @@ public function setSuffix($suffix) } /** - * Sets the company for the order address. - * - * @param string $company - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setCompany($company) { @@ -732,11 +660,7 @@ public function setCompany($company) } /** - * Sets the VAT ID for the order address. - * - * @param string $id - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setVatId($id) { @@ -744,11 +668,7 @@ public function setVatId($id) } /** - * Sets the VAT-is-valid flag value for the order address. - * - * @param int $vatIsValid - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setVatIsValid($vatIsValid) { @@ -756,11 +676,7 @@ public function setVatIsValid($vatIsValid) } /** - * Sets the VAT request ID for the order address. - * - * @param string $id - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setVatRequestId($id) { @@ -768,11 +684,7 @@ public function setVatRequestId($id) } /** - * Set region code - * - * @param string $regionCode - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setRegionCode($regionCode) { @@ -780,11 +692,7 @@ public function setRegionCode($regionCode) } /** - * Sets the VAT request date for the order address. - * - * @param string $vatRequestDate - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setVatRequestDate($vatRequestDate) { @@ -792,11 +700,7 @@ public function setVatRequestDate($vatRequestDate) } /** - * Sets the VAT-request-success flag value for the order address. - * - * @param int $vatRequestSuccess - * - * @return \Magento\Framework\DataObject|Address + * @inheritdoc */ public function setVatRequestSuccess($vatRequestSuccess) { @@ -804,9 +708,7 @@ public function setVatRequestSuccess($vatRequestSuccess) } /** - * Retrieve existing extension attributes object or create a new one. - * - * @return \Magento\Sales\Api\Data\OrderAddressExtensionInterface|null + * @inheritdoc */ public function getExtensionAttributes() { @@ -814,10 +716,7 @@ public function getExtensionAttributes() } /** - * Set an extension attributes object. - * - * @param \Magento\Sales\Api\Data\OrderAddressExtensionInterface $extensionAttributes - * @return $this + * @inheritdoc */ public function setExtensionAttributes(\Magento\Sales\Api\Data\OrderAddressExtensionInterface $extensionAttributes) { diff --git a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php index 8f9f275de1658..69e79d71db98b 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php @@ -942,11 +942,7 @@ public function getCreatedAt() } /** - * Gets an array of child transactions for the transaction. - * - * @param string $createdAt - * - * @return TransactionInterface|Transaction + * @inheritdoc */ public function setCreatedAt($createdAt) { @@ -954,11 +950,7 @@ public function setCreatedAt($createdAt) } /** - * Sets the parent ID for the transaction. - * - * @param int $id - * - * @return TransactionInterface|Transaction + * @inheritdoc */ public function setParentId($id) { @@ -966,11 +958,7 @@ public function setParentId($id) } /** - * Sets the order ID for the transaction. - * - * @param int $id - * - * @return TransactionInterface|Transaction + * @inheritdoc */ public function setOrderId($id) { @@ -978,11 +966,7 @@ public function setOrderId($id) } /** - * Sets the payment ID for the transaction. - * - * @param int $id - * - * @return TransactionInterface|Transaction + * @inheritdoc */ public function setPaymentId($id) { @@ -990,11 +974,7 @@ public function setPaymentId($id) } /** - * Sets the value of the is-closed flag for the transaction. - * - * @param int $isClosed - * - * @return TransactionInterface|Transaction + * @inheritdoc */ public function setIsClosed($isClosed) { @@ -1002,9 +982,7 @@ public function setIsClosed($isClosed) } /** - * Retrieve existing extension attributes object or create a new one. - * - * @return \Magento\Sales\Api\Data\TransactionExtensionInterface|null + * @inheritdoc */ public function getExtensionAttributes() { @@ -1012,10 +990,7 @@ public function getExtensionAttributes() } /** - * Set an extension attributes object. - * - * @param \Magento\Sales\Api\Data\TransactionExtensionInterface $extensionAttributes - * @return $this + * @inheritdoc */ public function setExtensionAttributes(\Magento\Sales\Api\Data\TransactionExtensionInterface $extensionAttributes) { diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index c212ebfec845f..ab9b356e7a748 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -1386,10 +1386,7 @@ public function getScopeType() } /** - * Get Scope Type Name - * - * @return string - * @since 100.1.0 + * @inheritdoc */ public function getScopeTypeName() { @@ -1397,9 +1394,7 @@ public function getScopeTypeName() } /** - * Retrieve existing extension attributes object or create a new one. - * - * @return \Magento\Store\Api\Data\StoreExtensionInterface|null + * @inheritdoc */ public function getExtensionAttributes() { From c58160625d6bca174b0751f1b671b5ec195b2637 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Fri, 5 Oct 2018 12:27:10 +0300 Subject: [PATCH 041/704] MAGETWO-93769: Catalog performance improvements --- app/code/Magento/Catalog/Model/ProductIdLocator.php | 2 +- .../Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ProductIdLocator.php b/app/code/Magento/Catalog/Model/ProductIdLocator.php index 4c4e6b416527b..8c42821d31697 100644 --- a/app/code/Magento/Catalog/Model/ProductIdLocator.php +++ b/app/code/Magento/Catalog/Model/ProductIdLocator.php @@ -86,7 +86,7 @@ public function retrieveProductIdsBySkus(array $skus) $pages = $collection->getLastPageNumber(); for ($currentPage = 1; $currentPage <= $pages; $currentPage++) { $collection->setCurPage($currentPage); - foreach ($collection->getIterator() as $item) { + foreach ($collection->getItems() as $item) { $sku = strtolower(trim($item->getSku())); $itemIdentifier = $item->getData($linkField); $this->idsBySku[$sku][$itemIdentifier] = $item->getTypeId(); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php index 6455aa58dd234..b9cb82274c808 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php @@ -60,7 +60,7 @@ public function testRetrieveProductIdsBySkus() $collection = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\Collection::class) ->setMethods( [ - 'getIterator', + 'getItems', 'addFieldToFilter', 'setPageSize', 'getLastPageNumber', @@ -78,7 +78,7 @@ public function testRetrieveProductIdsBySkus() $this->collectionFactory->expects($this->once())->method('create')->willReturn($collection); $collection->expects($this->once())->method('addFieldToFilter') ->with(\Magento\Catalog\Api\Data\ProductInterface::SKU, ['in' => $skus])->willReturnSelf(); - $collection->expects($this->atLeastOnce())->method('getIterator')->willReturn(new \ArrayIterator([$product])); + $collection->expects($this->atLeastOnce())->method('getItems')->willReturn([$product]); $collection->expects($this->atLeastOnce())->method('setPageSize')->willReturnSelf(); $collection->expects($this->atLeastOnce())->method('getLastPageNumber')->willReturn(1); $collection->expects($this->atLeastOnce())->method('setCurPage')->with(1)->willReturnSelf(); From 5fb7cf4c4f97255622f413a4a33667ae093e032d Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Fri, 5 Oct 2018 15:24:45 +0300 Subject: [PATCH 042/704] MAGETWO-93769: Catalog performance improvements --- .../Catalog/Model/Product/Price/TierPriceStorage.php | 8 ++++---- app/code/Magento/Catalog/Model/ProductIdLocator.php | 12 +++++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php index 7298e650bd448..3ee064670a460 100644 --- a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php +++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php @@ -97,7 +97,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function get(array $skus) { @@ -107,7 +107,7 @@ public function get(array $skus) } /** - * {@inheritdoc} + * @inheritdoc */ public function update(array $prices) { @@ -128,7 +128,7 @@ public function update(array $prices) } /** - * {@inheritdoc} + * @inheritdoc */ public function replace(array $prices) { @@ -144,7 +144,7 @@ public function replace(array $prices) } /** - * {@inheritdoc} + * @inheritdoc */ public function delete(array $prices) { diff --git a/app/code/Magento/Catalog/Model/ProductIdLocator.php b/app/code/Magento/Catalog/Model/ProductIdLocator.php index 8c42821d31697..2d382164f2649 100644 --- a/app/code/Magento/Catalog/Model/ProductIdLocator.php +++ b/app/code/Magento/Catalog/Model/ProductIdLocator.php @@ -63,7 +63,17 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc + * + * Load product items by provided products SKUs. + * Products collection will be iterated by pages with the $this->batchSize as a page size (for a cases when to many + * products SKUs were provided in parameters. + * Loaded products will be chached in the $this->idsBySku variable, but in the end of the method these storage will + * be truncated to $idsLimit quantity. + * As a result array with the products data will be returned with the following scheme: + * $data['product_sku']['link_field_value' => 'product_type'] + * + * @throws \Exception */ public function retrieveProductIdsBySkus(array $skus) { From 468b290efea26bb305ea9a81f80c8eca05ed4074 Mon Sep 17 00:00:00 2001 From: Pablo Fantini Date: Fri, 5 Oct 2018 09:54:38 -0300 Subject: [PATCH 043/704] GraphQL-57: Curate address data on customerDataProvider --- .../Resolver/Address/AddressDataProvider.php | 8 +++++--- .../Customer/CustomerDataProvider.php | 20 +++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php index 2b0cbe036ada7..9726e8c0564a9 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Address/AddressDataProvider.php @@ -64,16 +64,18 @@ public function __construct( * * @param array $address * @param AddressInterface $addressObject - * @return null + * @return array */ private function curateAddressDefaultValues(array $address, AddressInterface $addressObject) : array { $customerModel = $this->customerFactory->create(); $this->customerResourceModel->load($customerModel, $addressObject->getCustomerId()); $address[CustomerInterface::DEFAULT_BILLING] = - ($addressObject->getId() == $customerModel->getDefaultBillingAddress()->getId()) ? true : false; + ($customerModel->getDefaultBillingAddress() + && $addressObject->getId() == $customerModel->getDefaultBillingAddress()->getId()) ? true : false; $address[CustomerInterface::DEFAULT_SHIPPING] = - ($addressObject->getId() == $customerModel->getDefaultShippingAddress()->getId()) ? true : false; + ($customerModel->getDefaultShippingAddress() + && $addressObject->getId() == $customerModel->getDefaultShippingAddress()->getId()) ? true : false; return $address; } diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php index 1a942a2ab149a..4f238c71cfc6b 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/CustomerDataProvider.php @@ -67,6 +67,25 @@ public function getCustomerById(int $customerId) : array return $this->processCustomer($customerObject); } + /** + * Curate default shipping and default billing keys + * + * @param array $arrayAddress + * @return array + */ + private function curateAddressData(array $arrayAddress) : array + { + foreach ($arrayAddress as $key => $address) { + if (!isset($address['default_shipping'])) { + $arrayAddress[$key]['default_shipping'] = false; + } + if (!isset($address['default_billing'])) { + $arrayAddress[$key]['default_billing'] = false; + } + } + return $arrayAddress; + } + /** * Transform single customer data from object to in array format * @@ -80,6 +99,7 @@ private function processCustomer(CustomerInterface $customerObject) : array CustomerRepositoryInterface::class, 'get' ); + $customer['addresses'] = $this->curateAddressData($customer['addresses']); if (isset($customer['extension_attributes'])) { $customer = array_merge($customer, $customer['extension_attributes']); } From d2c2c0067d19b1624e757776695ad19be4c49f52 Mon Sep 17 00:00:00 2001 From: Yevhenii Dumskyi Date: Sat, 6 Oct 2018 12:59:45 +0300 Subject: [PATCH 044/704] Fix no results handle layout update --- .../Controller/Advanced/Result.php | 4 +++- .../CatalogSearch/Controller/Result/Index.php | 4 +++- .../Unit/Controller/Advanced/ResultTest.php | 24 +++++++++++++++---- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php b/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php index dfbd46aa6a1cf..6cefacaf785e9 100644 --- a/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php +++ b/app/code/Magento/CatalogSearch/Controller/Advanced/Result.php @@ -64,7 +64,9 @@ public function execute() $handles = null; if ($size == 0) { - $handles = [static::DEFAULT_NO_RESULT_HANDLE]; + $this->_view->getPage()->initLayout(); + $handles = $this->_view->getLayout()->getUpdate()->getHandles(); + $handles[] = static::DEFAULT_NO_RESULT_HANDLE; } $this->_view->loadLayout($handles); diff --git a/app/code/Magento/CatalogSearch/Controller/Result/Index.php b/app/code/Magento/CatalogSearch/Controller/Result/Index.php index 3e0569ef7bfe8..a016beb0215b4 100644 --- a/app/code/Magento/CatalogSearch/Controller/Result/Index.php +++ b/app/code/Magento/CatalogSearch/Controller/Result/Index.php @@ -95,7 +95,9 @@ public function execute() $handles = null; if ($query->getNumResults() == 0) { - $handles = [static::DEFAULT_NO_RESULT_HANDLE]; + $this->_view->getPage()->initLayout(); + $handles = $this->_view->getLayout()->getUpdate()->getHandles(); + $handles[] = static::DEFAULT_NO_RESULT_HANDLE; } if (empty($getAdditionalRequestParameters) && diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php index d4229ff934e9b..71ba6589eee09 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Controller/Advanced/ResultTest.php @@ -139,9 +139,10 @@ public function testUrlSetOnException() /** @var \Magento\CatalogSearch\Controller\Advanced\Result $instance */ $instance = $objectManager->getObject( \Magento\CatalogSearch\Controller\Advanced\Result::class, - ['context' => $contextMock, - 'catalogSearchAdvanced' => $catalogSearchAdvanced, - 'urlFactory' => $urlFactoryMock + [ + 'context' => $contextMock, + 'catalogSearchAdvanced' => $catalogSearchAdvanced, + 'urlFactory' => $urlFactoryMock ] ); $this->assertEquals($redirectResultMock, $instance->execute()); @@ -151,10 +152,25 @@ public function testNoResultsHandle() { $expectedQuery = 'notExistTerm'; - $view = $this->createPartialMock(\Magento\Framework\App\View::class, ['loadLayout', 'renderLayout']); + $update = $this->createPartialMock(\Magento\Framework\View\Model\Layout\Merge::class, ['getHandles']); + $update->expects($this->once())->method('getHandles')->will($this->returnValue([])); + + $layout = $this->createPartialMock(\Magento\Framework\View\Result\Layout::class, ['getUpdate']); + $layout->expects($this->once())->method('getUpdate')->will($this->returnValue($update)); + + $page = $this->createPartialMock(\Magento\Framework\View\Result\Page::class, ['initLayout']); + + $view = $this->createPartialMock( + \Magento\Framework\App\View::class, + ['loadLayout', 'renderLayout', 'getPage', 'getLayout'] + ); + $view->expects($this->once())->method('loadLayout') ->with([\Magento\CatalogSearch\Controller\Advanced\Result::DEFAULT_NO_RESULT_HANDLE]); + $view->expects($this->once())->method('getPage')->will($this->returnValue($page)); + $view->expects($this->once())->method('getLayout')->will($this->returnValue($layout)); + $request = $this->createPartialMock(\Magento\Framework\App\Console\Request::class, ['getQueryValue']); $request->expects($this->once())->method('getQueryValue')->will($this->returnValue($expectedQuery)); From ad50297e7bf4ea39db62f0969579a1ca8eb5f1a3 Mon Sep 17 00:00:00 2001 From: Graham Wharton Date: Mon, 8 Oct 2018 15:25:11 +0100 Subject: [PATCH 045/704] Removed setFromByStore and replaced with default parameter to setFrom --- .../Sales/Model/Order/Email/SenderBuilder.php | 16 +---- .../Model/Order/Email/SenderBuilderTest.php | 25 ++++---- .../Mail/Template/TransportBuilder.php | 5 +- .../Mail/Template/TransportBuilderByStore.php | 54 ---------------- .../Template/TransportBuilderByStoreTest.php | 64 ------------------- .../Unit/Template/TransportBuilderTest.php | 5 +- 6 files changed, 21 insertions(+), 148 deletions(-) delete mode 100644 lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php delete mode 100644 lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php diff --git a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php index 7ec089b882972..e5c9c4b4afddc 100644 --- a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php +++ b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php @@ -5,9 +5,7 @@ */ namespace Magento\Sales\Model\Order\Email; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Mail\Template\TransportBuilder; -use Magento\Framework\Mail\Template\TransportBuilderByStore; use Magento\Sales\Model\Order\Email\Container\IdentityInterface; use Magento\Sales\Model\Order\Email\Container\Template; @@ -28,29 +26,19 @@ class SenderBuilder */ protected $transportBuilder; - /** - * @var TransportBuilderByStore - */ - private $transportBuilderByStore; - /** * @param Template $templateContainer * @param IdentityInterface $identityContainer * @param TransportBuilder $transportBuilder - * @param TransportBuilderByStore $transportBuilderByStore */ public function __construct( Template $templateContainer, IdentityInterface $identityContainer, - TransportBuilder $transportBuilder, - TransportBuilderByStore $transportBuilderByStore = null + TransportBuilder $transportBuilder ) { $this->templateContainer = $templateContainer; $this->identityContainer = $identityContainer; $this->transportBuilder = $transportBuilder; - $this->transportBuilderByStore = $transportBuilderByStore ?: ObjectManager::getInstance()->get( - TransportBuilderByStore::class - ); } /** @@ -110,7 +98,7 @@ protected function configureEmailTemplate() $this->transportBuilder->setTemplateIdentifier($this->templateContainer->getTemplateId()); $this->transportBuilder->setTemplateOptions($this->templateContainer->getTemplateOptions()); $this->transportBuilder->setTemplateVars($this->templateContainer->getTemplateVars()); - $this->transportBuilderByStore->setFromByStore( + $this->transportBuilder->setFrom( $this->identityContainer->getEmailIdentity(), $this->identityContainer->getStore()->getId() ); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php index 38209bb22aef4..a3b7f6ef574dc 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php @@ -6,7 +6,6 @@ namespace Magento\Sales\Test\Unit\Model\Order\Email; -use Magento\Framework\Mail\Template\TransportBuilderByStore; use Magento\Sales\Model\Order\Email\SenderBuilder; class SenderBuilderTest extends \PHPUnit\Framework\TestCase @@ -36,11 +35,6 @@ class SenderBuilderTest extends \PHPUnit\Framework\TestCase */ private $storeMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $transportBuilderByStore; - protected function setUp() { $templateId = 'test_template_id'; @@ -82,11 +76,10 @@ protected function setUp() 'setTemplateIdentifier', 'setTemplateOptions', 'setTemplateVars', + 'setFrom', ] ); - $this->transportBuilderByStore = $this->createMock(TransportBuilderByStore::class); - $this->templateContainerMock->expects($this->once()) ->method('getTemplateId') ->will($this->returnValue($templateId)); @@ -109,8 +102,8 @@ protected function setUp() $this->identityContainerMock->expects($this->once()) ->method('getEmailIdentity') ->will($this->returnValue($emailIdentity)); - $this->transportBuilderByStore->expects($this->once()) - ->method('setFromByStore') + $this->transportBuilder->expects($this->once()) + ->method('setFrom') ->with($this->equalTo($emailIdentity)); $this->identityContainerMock->expects($this->once()) @@ -120,8 +113,7 @@ protected function setUp() $this->senderBuilder = new SenderBuilder( $this->templateContainerMock, $this->identityContainerMock, - $this->transportBuilder, - $this->transportBuilderByStore + $this->transportBuilder ); } @@ -129,6 +121,8 @@ public function testSend() { $customerName = 'test_name'; $customerEmail = 'test_email'; + $identity = 'email_identity_test'; + $transportMock = $this->createMock( \Magento\Sales\Test\Unit\Model\Order\Email\Stub\TransportInterfaceMock::class ); @@ -151,6 +145,9 @@ public function testSend() $this->storeMock->expects($this->once()) ->method('getId') ->willReturn(1); + $this->transportBuilder->expects($this->once()) + ->method('setFrom') + ->with($identity, 1); $this->transportBuilder->expects($this->once()) ->method('addTo') ->with($this->equalTo($customerEmail), $this->equalTo($customerName)); @@ -164,6 +161,7 @@ public function testSend() public function testSendCopyTo() { + $identity = 'email_identity_test'; $transportMock = $this->createMock( \Magento\Sales\Test\Unit\Model\Order\Email\Stub\TransportInterfaceMock::class ); @@ -177,6 +175,9 @@ public function testSendCopyTo() $this->transportBuilder->expects($this->once()) ->method('addTo') ->with($this->equalTo('example@mail.com')); + $this->transportBuilder->expects($this->once()) + ->method('setFrom') + ->with($identity, 1); $this->identityContainerMock->expects($this->once()) ->method('getStore') ->willReturn($this->storeMock); diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index eac5d74c6e3dc..4a34f9abff340 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -176,11 +176,12 @@ public function setReplyTo($email, $name = null) * Set mail from address * * @param string|array $from + * @param string|int $store * @return $this */ - public function setFrom($from) + public function setFrom($from, $store = null) { - $result = $this->_senderResolver->resolve($from); + $result = $this->_senderResolver->resolve($from, $store); $this->message->setFrom($result['email'], $result['name']); return $this; } diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php deleted file mode 100644 index 785c93824a57d..0000000000000 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php +++ /dev/null @@ -1,54 +0,0 @@ -message = $message; - $this->senderResolver = $senderResolver; - } - - /** - * Set mail from address by store. - * - * @param string|array $from - * @param string|int $store - * - * @return $this - */ - public function setFromByStore($from, $store) - { - $result = $this->senderResolver->resolve($from, $store); - $this->message->setFrom($result['email'], $result['name']); - - return $this; - } -} diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php deleted file mode 100644 index 80df2887a3a93..0000000000000 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php +++ /dev/null @@ -1,64 +0,0 @@ -messageMock = $this->createMock(\Magento\Framework\Mail\Message::class); - $this->senderResolverMock = $this->createMock(\Magento\Framework\Mail\Template\SenderResolverInterface::class); - - $this->model = $objectManagerHelper->getObject( - TransportBuilderByStore::class, - [ - 'message' => $this->messageMock, - 'senderResolver' => $this->senderResolverMock, - ] - ); - } - - /** - * @return void - */ - public function testSetFromByStore() - { - $sender = ['email' => 'from@example.com', 'name' => 'name']; - $store = 1; - $this->senderResolverMock->expects($this->once()) - ->method('resolve') - ->with($sender, $store) - ->willReturn($sender); - $this->messageMock->expects($this->once()) - ->method('setFrom') - ->with('from@example.com', 'name') - ->willReturnSelf(); - - $this->model->setFromByStore($sender, $store); - } -} diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php index 596640480c823..59560b4cbe2da 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php @@ -170,16 +170,17 @@ public function getTransportDataProvider() public function testSetFrom() { $sender = ['email' => 'from@example.com', 'name' => 'name']; + $store = 1; $this->senderResolverMock->expects($this->once()) ->method('resolve') - ->with($sender) + ->with($sender, $store) ->willReturn($sender); $this->messageMock->expects($this->once()) ->method('setFrom') ->with('from@example.com', 'name') ->willReturnSelf(); - $this->builder->setFrom($sender); + $this->builder->setFrom($sender, $store); } /** From e16ff74e4f5f24a4a2530b6aced6ef50032515e0 Mon Sep 17 00:00:00 2001 From: gwharton <30697781+gwharton@users.noreply.github.com> Date: Tue, 9 Oct 2018 11:26:02 +0100 Subject: [PATCH 046/704] Added annotation block to Sender Builder --- app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php index e5c9c4b4afddc..e09e13ccded24 100644 --- a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php +++ b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php @@ -9,6 +9,9 @@ use Magento\Sales\Model\Order\Email\Container\IdentityInterface; use Magento\Sales\Model\Order\Email\Container\Template; +/** + * Sender Builder + */ class SenderBuilder { /** From 621d2cfeaf8e0a15e592cdc001c856385b3cbd83 Mon Sep 17 00:00:00 2001 From: gwharton <30697781+gwharton@users.noreply.github.com> Date: Tue, 9 Oct 2018 11:27:08 +0100 Subject: [PATCH 047/704] Added short description to annotation block --- .../Magento/Framework/Mail/Template/TransportBuilder.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index 4a34f9abff340..89f770cad4022 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -17,6 +17,8 @@ use Magento\Framework\Phrase; /** + * TransportBuilder + * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ From 1ce85bb8d637310849b2487a035952bf3d23dd20 Mon Sep 17 00:00:00 2001 From: Volodymyr Hryvinskyi Date: Wed, 10 Oct 2018 13:34:29 +0300 Subject: [PATCH 048/704] update comments --- .../Ui/DataProvider/NotificationDataProvider.php | 9 ++++++--- app/code/Magento/Sales/Model/Order/Address.php | 6 +++++- .../Magento/Sales/Model/Order/Payment/Transaction.php | 4 +++- app/code/Magento/Store/Model/Store.php | 6 ++++-- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php b/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php index 8418b4f9a753d..737ec1caa7fb1 100644 --- a/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php +++ b/app/code/Magento/ReleaseNotification/Ui/DataProvider/NotificationDataProvider.php @@ -133,7 +133,8 @@ public function setConfigData($config) } /** - * @inheritdoc + * {@inheritdoc} + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getFieldMetaInfo($fieldSetName, $fieldName) { @@ -141,7 +142,8 @@ public function getFieldMetaInfo($fieldSetName, $fieldName) } /** - * @inheritdoc + * {@inheritdoc} + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getFieldSetMetaInfo($fieldSetName) { @@ -149,7 +151,8 @@ public function getFieldSetMetaInfo($fieldSetName) } /** - * @inheritdoc + * {@inheritdoc} + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getFieldsMetaInfo($fieldSetName) { diff --git a/app/code/Magento/Sales/Model/Order/Address.php b/app/code/Magento/Sales/Model/Order/Address.php index a19c587216b1c..fcaffb0407b4c 100644 --- a/app/code/Magento/Sales/Model/Order/Address.php +++ b/app/code/Magento/Sales/Model/Order/Address.php @@ -716,7 +716,11 @@ public function getExtensionAttributes() } /** - * @inheritdoc + * {@inheritdoc} + * + * @param \Magento\Sales\Api\Data\OrderAddressExtensionInterface $extensionAttributes + * + * @return $this */ public function setExtensionAttributes(\Magento\Sales\Api\Data\OrderAddressExtensionInterface $extensionAttributes) { diff --git a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php index 69e79d71db98b..8b8865bab2b71 100644 --- a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php +++ b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php @@ -982,7 +982,9 @@ public function setIsClosed($isClosed) } /** - * @inheritdoc + * {@inheritdoc} + * + * @return \Magento\Sales\Api\Data\TransactionExtensionInterface|null */ public function getExtensionAttributes() { diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index ab9b356e7a748..b078942b9e932 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -1378,7 +1378,8 @@ public function getStorePath() } /** - * @inheritdoc + * {@inheritdoc} + * @since 100.1.0 */ public function getScopeType() { @@ -1386,7 +1387,8 @@ public function getScopeType() } /** - * @inheritdoc + * {@inheritdoc} + * @since 100.1.0 */ public function getScopeTypeName() { From de39e254481b35963d058ab5a89976f485def882 Mon Sep 17 00:00:00 2001 From: vprohorov Date: Fri, 12 Oct 2018 14:15:18 +0300 Subject: [PATCH 049/704] MAGETWO-94444: [2.3] Order total value is limited by 8 round digits - Changing precision in database schema --- app/code/Magento/SalesRule/etc/db_schema.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/SalesRule/etc/db_schema.xml b/app/code/Magento/SalesRule/etc/db_schema.xml index 145ed0c96d95f..e84d92df168d6 100644 --- a/app/code/Magento/SalesRule/etc/db_schema.xml +++ b/app/code/Magento/SalesRule/etc/db_schema.xml @@ -208,17 +208,17 @@ - - - - @@ -250,17 +250,17 @@ - - - - @@ -292,11 +292,11 @@ - - From b6f9e48289119117df230b6578ac9ff3d69e4996 Mon Sep 17 00:00:00 2001 From: Graham Wharton Date: Tue, 16 Oct 2018 09:39:17 +0100 Subject: [PATCH 050/704] Added new setFromByStore function and deprecated old function. TODO : Rest of Magento codebase still uses deprecated setFrom --- .../Sales/Model/Order/Email/SenderBuilder.php | 2 +- .../Model/Order/Email/SenderBuilderTest.php | 10 +++++----- .../Mail/Template/TransportBuilder.php | 17 ++++++++++++++++- .../Test/Unit/Template/TransportBuilderTest.php | 4 ++-- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php index e09e13ccded24..98bd942cd0b10 100644 --- a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php +++ b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php @@ -101,7 +101,7 @@ protected function configureEmailTemplate() $this->transportBuilder->setTemplateIdentifier($this->templateContainer->getTemplateId()); $this->transportBuilder->setTemplateOptions($this->templateContainer->getTemplateOptions()); $this->transportBuilder->setTemplateVars($this->templateContainer->getTemplateVars()); - $this->transportBuilder->setFrom( + $this->transportBuilder->setFromByStore( $this->identityContainer->getEmailIdentity(), $this->identityContainer->getStore()->getId() ); diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php index a3b7f6ef574dc..86d65cc476668 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php @@ -76,7 +76,7 @@ protected function setUp() 'setTemplateIdentifier', 'setTemplateOptions', 'setTemplateVars', - 'setFrom', + 'setFromByStore', ] ); @@ -103,8 +103,8 @@ protected function setUp() ->method('getEmailIdentity') ->will($this->returnValue($emailIdentity)); $this->transportBuilder->expects($this->once()) - ->method('setFrom') - ->with($this->equalTo($emailIdentity)); + ->method('setFromByStore') + ->with($this->equalTo($emailIdentity), 1); $this->identityContainerMock->expects($this->once()) ->method('getEmailCopyTo') @@ -146,7 +146,7 @@ public function testSend() ->method('getId') ->willReturn(1); $this->transportBuilder->expects($this->once()) - ->method('setFrom') + ->method('setFromByStore') ->with($identity, 1); $this->transportBuilder->expects($this->once()) ->method('addTo') @@ -176,7 +176,7 @@ public function testSendCopyTo() ->method('addTo') ->with($this->equalTo('example@mail.com')); $this->transportBuilder->expects($this->once()) - ->method('setFrom') + ->method('setFromByStore') ->with($identity, 1); $this->identityContainerMock->expects($this->once()) ->method('getStore') diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index 89f770cad4022..9343adfecf58d 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -177,8 +177,9 @@ public function setReplyTo($email, $name = null) /** * Set mail from address * + * @deprecated Use setFromByStore + * * @param string|array $from - * @param string|int $store * @return $this */ public function setFrom($from, $store = null) @@ -188,6 +189,20 @@ public function setFrom($from, $store = null) return $this; } + /** + * Set mail from address by store + * + * @param string|array $from + * @param string|int $store + * @return $this + */ + public function setFromByStore($from, $store = null) + { + $result = $this->_senderResolver->resolve($from, $store); + $this->message->setFrom($result['email'], $result['name']); + return $this; + } + /** * Set template identifier * diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php index 59560b4cbe2da..32bd536f96c9d 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php @@ -176,11 +176,11 @@ public function testSetFrom() ->with($sender, $store) ->willReturn($sender); $this->messageMock->expects($this->once()) - ->method('setFrom') + ->method('setFromByStore') ->with('from@example.com', 'name') ->willReturnSelf(); - $this->builder->setFrom($sender, $store); + $this->builder->setFromByStore($sender, $store); } /** From 0dc85785e522e2be8c72dfeaee7e9f5054195034 Mon Sep 17 00:00:00 2001 From: Graham Wharton Date: Tue, 16 Oct 2018 10:13:30 +0100 Subject: [PATCH 051/704] Removed code duplication --- .../Framework/Mail/Template/TransportBuilder.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index 9343adfecf58d..7d29500bcd983 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -177,16 +177,17 @@ public function setReplyTo($email, $name = null) /** * Set mail from address * - * @deprecated Use setFromByStore + * @deprecated This function sets the from address for the first store only. + * new function setFromByStore introduced to allow setting of from address + * based on store. + * @see setFromByStore() * * @param string|array $from * @return $this */ - public function setFrom($from, $store = null) + public function setFrom($from) { - $result = $this->_senderResolver->resolve($from, $store); - $this->message->setFrom($result['email'], $result['name']); - return $this; + return($this->setFromByStore($from)); } /** From 122019c953037df74ef97245a42b274eae2e6639 Mon Sep 17 00:00:00 2001 From: Graham Wharton Date: Tue, 16 Oct 2018 10:22:04 +0100 Subject: [PATCH 052/704] Fixed TransportBuilderTest --- .../Mail/Test/Unit/Template/TransportBuilderTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php index 32bd536f96c9d..5f20a790a2d32 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php @@ -167,7 +167,7 @@ public function getTransportDataProvider() /** * @return void */ - public function testSetFrom() + public function testSetFromByStore() { $sender = ['email' => 'from@example.com', 'name' => 'name']; $store = 1; @@ -176,7 +176,7 @@ public function testSetFrom() ->with($sender, $store) ->willReturn($sender); $this->messageMock->expects($this->once()) - ->method('setFromByStore') + ->method('setFrom') ->with('from@example.com', 'name') ->willReturnSelf(); From 193c6c2f6facf7276c1c8858d29ea3b847062ef5 Mon Sep 17 00:00:00 2001 From: Graham Wharton Date: Tue, 16 Oct 2018 10:24:54 +0100 Subject: [PATCH 053/704] Fixed whitespace violations --- .../Magento/Framework/Mail/Template/TransportBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index 7d29500bcd983..bb6727043f9b5 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -181,7 +181,7 @@ public function setReplyTo($email, $name = null) * new function setFromByStore introduced to allow setting of from address * based on store. * @see setFromByStore() - * + * * @param string|array $from * @return $this */ From ff40c182383a75842e915db1c3aeb6ef8476c596 Mon Sep 17 00:00:00 2001 From: bshevchenko <1408sheva@gmail.com> Date: Tue, 16 Oct 2018 12:47:30 +0300 Subject: [PATCH 054/704] MAGETWO-95169: Add Bundle product with zero price to shopping cart --- .../StorefrontProductCartActionGroup.xml | 14 ++++ ...ProductWithZeroPriceToShoppingCartTest.xml | 77 +++++++++++++++++++ ...ctWithCustomOptionsWithLongValuesTitle.xml | 3 + .../Mftf/ActionGroup/CheckoutActionGroup.xml | 17 +++- .../Mftf/Section/AdminOrdersGridSection.xml | 2 +- 5 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml index f28ffbdc40acc..e36730a87b41a 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml @@ -24,4 +24,18 @@ + + + + + + + + + + + + + + diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml new file mode 100644 index 0000000000000..71aaf76c42e84 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml @@ -0,0 +1,77 @@ + + + + + + + + + + <description value="Add Bundle product with zero price to shopping cart"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-95167"/> + <group value="bundle"/> + </annotations> + <before> + <!--Enable freeShipping--> + <createData entity="FreeShippinMethodConfig" stepKey="enableFreeShipping"/> + <!--Create category--> + <createData entity="SimpleSubCategory" stepKey="createSubCategory"/> + <!--Create simple with zero price product--> + <createData entity="ApiProductWithDescription" stepKey="apiSimple"> + <field key="price">0</field> + </createData> + <!--Create Bundle product--> + <createData entity="ApiBundleProductPriceViewRange" stepKey="apiBundleProduct"> + <requiredEntity createDataKey="createSubCategory"/> + </createData> + <!--Create Attribute--> + <createData entity="DropDownBundleOption" stepKey="bundleOption"> + <requiredEntity createDataKey="apiBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="createBundleLink"> + <requiredEntity createDataKey="apiBundleProduct"/> + <requiredEntity createDataKey="bundleOption"/> + <requiredEntity createDataKey="apiSimple"/> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + </before> + <after> + <createData entity="FreeShippinMethodDefault" stepKey="disableFreeShipping"/> + <deleteData createDataKey="apiSimple" stepKey="deleteSimple"/> + <deleteData createDataKey="apiBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="createSubCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Open category page--> + <amOnPage url="{{StorefrontCategoryPage.url($$createSubCategory.custom_attributes[url_key]$$)}}" stepKey="amOnCategoryPage"/> + <!--Add bundle product to cart--> + <actionGroup ref="StorefrontAddBundleProductFromCategoryToCartActionGroup" stepKey="addBundleProductToCart"> + <argument name="productName" value="$$apiBundleProduct.name$$"/> + </actionGroup> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + + <!--Place order--> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShipping"> + <argument name="shippingMethod" value="Free Shipping"/> + </actionGroup> + <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="checkoutPlaceOrder"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> + + <!--Check subtotal in created order--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="filterOrderGridById" stepKey="filterOrderById"> + <argument name="orderId" value="$grabOrderNumber"/> + </actionGroup> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <scrollTo selector="{{AdminOrderTotalSection.subTotal}}" stepKey="scrollToOrderTotalSection"/> + <see selector="{{AdminOrderTotalSection.subTotal}}" userInput="$0.00" stepKey="checkSubtotal"/> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml index a03636e52ee97..32d141d7e533e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -33,6 +33,8 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> + <actionGroup ref="logout" stepKey="logout"/> </after> <!-- Login Customer Storefront --> @@ -86,6 +88,7 @@ <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnOrdersPage"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters"/> <fillField selector="{{AdminOrdersGridSection.search}}" userInput="{$grabOrderNumber}" stepKey="fillOrderNum"/> <click selector="{{AdminOrdersGridSection.submitSearch}}" stepKey="submitSearchOrderNum"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappearOnSearch"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml index 32d6ad8667029..d3dbf34c010c8 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml @@ -19,8 +19,10 @@ <!-- Guest checkout filling shipping section --> <actionGroup name="GuestCheckoutFillingShippingSectionActionGroup"> <arguments> - <argument name="customerVar"/> - <argument name="customerAddressVar"/> + <argument name="customerVar" defaultValue="CustomerEntityOne"/> + <argument name="customerAddressVar" defaultValue="CustomerAddressSimple"/> + <!--First available shipping method will be selected if value is not passed for shippingMethod--> + <argument name="shippingMethod" defaultValue="" type="string"/> </arguments> <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{customerVar.email}}" stepKey="enterEmail"/> <fillField selector="{{CheckoutShippingSection.firstName}}" userInput="{{customerVar.firstname}}" stepKey="enterFirstName"/> @@ -31,7 +33,7 @@ <fillField selector="{{CheckoutShippingSection.postcode}}" userInput="{{customerAddressVar.postcode}}" stepKey="enterPostcode"/> <fillField selector="{{CheckoutShippingSection.telephone}}" userInput="{{customerAddressVar.telephone}}" stepKey="enterTelephone"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> - <click selector="{{CheckoutShippingSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> + <click selector="{{CheckoutShippingMethodsSection.checkShippingMethodByName('shippingMethod')}}" stepKey="selectShippingMethod"/> <waitForElement selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoaded"/> @@ -154,6 +156,7 @@ <conditionalClick selector="{{CheckoutCartSummarySection.shippingHeading}}" dependentSelector="{{CheckoutCartSummarySection.country}}" visible="false" stepKey="openShippingDetails"/> <see selector="{{CheckoutCartSummarySection.countryParameterized('placeNumber')}}" userInput="{{country}}" stepKey="seeCountry"/> </actionGroup> + <actionGroup name="StorefrontSignOutActionGroup"> <click selector="{{StoreFrontSignOutSection.customerAccount}}" stepKey="clickCustomerButton"/> <click selector="{{StoreFrontSignOutSection.signOut}}" stepKey="clickToSignOut"/> @@ -161,4 +164,10 @@ <see userInput="You are signed out" stepKey="signOut"/> </actionGroup> -</actionGroups> \ No newline at end of file + <!--Click Place Order button--> + <actionGroup name="ClickPlaceOrderActionGroup"> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml index 7ece18fb863b7..5e36a8f776628 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml @@ -22,7 +22,7 @@ <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> <element name="rowViewAction" type="button" selector=".data-grid tbody > tr:nth-of-type({{row}}) .action-menu-item" parameterized="true" timeout="30"/> <element name="createNewOrder" type="button" selector=".page-actions-buttons button#add" timeout="30"/> - <element name="firstRow" type="button" selector="tr.data-row:nth-of-type(1)"/> + <element name="firstRow" type="button" selector="tr.data-row:nth-of-type(1)" timeout="30"/> <element name="columnHeader" type="button" selector="//div[@data-role='grid-wrapper']//table[contains(@class, 'data-grid')]/thead/tr/th[contains(@class, 'data-grid-th')]/span[text() = '{{label}}']" parameterized="true" timeout="30"/> <element name="gridCell" type="text" selector="//tr[{{row}}]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., '{{column}}')]/preceding-sibling::th) +1 ]" parameterized="true"/> <element name="viewBookmarkDropdown" type="button" selector="div.admin__data-grid-action-bookmarks button" timeout="30"/> From bd0aa57cc837918335015d051f08cf380dc186d0 Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Wed, 17 Oct 2018 08:05:58 +0300 Subject: [PATCH 055/704] magento/magento2#18471 Alternative fix for Multi Store Emails issue Fix small issue --- .../Magento/Framework/Mail/Template/TransportBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index bb6727043f9b5..3a9117deb4b17 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -187,7 +187,7 @@ public function setReplyTo($email, $name = null) */ public function setFrom($from) { - return($this->setFromByStore($from)); + return $this->setFromByStore($from, null); } /** From d225cef871e16937df45f3ea138debed41aadf8b Mon Sep 17 00:00:00 2001 From: bshevchenko <1408sheva@gmail.com> Date: Thu, 18 Oct 2018 10:45:49 +0300 Subject: [PATCH 056/704] MAGETWO-95169: Add Bundle product with zero price to shopping cart --- .../Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml index 5e36a8f776628..7ece18fb863b7 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml @@ -22,7 +22,7 @@ <element name="applyFilters" type="button" selector="button[data-action='grid-filter-apply']" timeout="30"/> <element name="rowViewAction" type="button" selector=".data-grid tbody > tr:nth-of-type({{row}}) .action-menu-item" parameterized="true" timeout="30"/> <element name="createNewOrder" type="button" selector=".page-actions-buttons button#add" timeout="30"/> - <element name="firstRow" type="button" selector="tr.data-row:nth-of-type(1)" timeout="30"/> + <element name="firstRow" type="button" selector="tr.data-row:nth-of-type(1)"/> <element name="columnHeader" type="button" selector="//div[@data-role='grid-wrapper']//table[contains(@class, 'data-grid')]/thead/tr/th[contains(@class, 'data-grid-th')]/span[text() = '{{label}}']" parameterized="true" timeout="30"/> <element name="gridCell" type="text" selector="//tr[{{row}}]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., '{{column}}')]/preceding-sibling::th) +1 ]" parameterized="true"/> <element name="viewBookmarkDropdown" type="button" selector="div.admin__data-grid-action-bookmarks button" timeout="30"/> From 523b0464966f81f1f01aa13073092bfddf363219 Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Thu, 18 Oct 2018 11:36:17 +0100 Subject: [PATCH 057/704] Reintroduced TransportBuilerByStore classes and marked deprecated --- .../Sales/Model/Order/Email/SenderBuilder.php | 7 +- .../Mail/Template/TransportBuilderByStore.php | 61 ++++++++++++++++++ .../Template/TransportBuilderByStoreTest.php | 64 +++++++++++++++++++ 3 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php create mode 100644 lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php diff --git a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php index 98bd942cd0b10..a7d749ec04c7d 100644 --- a/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php +++ b/app/code/Magento/Sales/Model/Order/Email/SenderBuilder.php @@ -6,6 +6,7 @@ namespace Magento\Sales\Model\Order\Email; use Magento\Framework\Mail\Template\TransportBuilder; +use Magento\Framework\Mail\Template\TransportBuilderByStore; use Magento\Sales\Model\Order\Email\Container\IdentityInterface; use Magento\Sales\Model\Order\Email\Container\Template; @@ -30,14 +31,18 @@ class SenderBuilder protected $transportBuilder; /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * * @param Template $templateContainer * @param IdentityInterface $identityContainer * @param TransportBuilder $transportBuilder + * @param TransportBuilderByStore $transportBuilderByStore */ public function __construct( Template $templateContainer, IdentityInterface $identityContainer, - TransportBuilder $transportBuilder + TransportBuilder $transportBuilder, + TransportBuilderByStore $transportBuilderByStore = null ) { $this->templateContainer = $templateContainer; $this->identityContainer = $identityContainer; diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php new file mode 100644 index 0000000000000..baac867fd6ce8 --- /dev/null +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Mail\Template; + +use Magento\Framework\Mail\MessageInterface; + +/** + * Class TransportBuilderByStore + * + * @deprecated The ability to set From address based on store is now available + * in the \Magento\Framework\Mail\Template\TransportBuilder class + * @see \Magento\Framework\Mail\Template\TransportBuilder::setFromByStore + */ +class TransportBuilderByStore +{ + /** + * Message. + * + * @var \Magento\Framework\Mail\Message + */ + protected $message; + + /** + * Sender resolver. + * + * @var \Magento\Framework\Mail\Template\SenderResolverInterface + */ + private $senderResolver; + + /** + * @param MessageInterface $message + * @param SenderResolverInterface $senderResolver + */ + public function __construct( + MessageInterface $message, + SenderResolverInterface $senderResolver + ) { + $this->message = $message; + $this->senderResolver = $senderResolver; + } + + /** + * Set mail from address by store. + * + * @param string|array $from + * @param string|int $store + * + * @return $this + */ + public function setFromByStore($from, $store) + { + $result = $this->senderResolver->resolve($from, $store); + $this->message->setFrom($result['email'], $result['name']); + + return $this; + } +} diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php new file mode 100644 index 0000000000000..80df2887a3a93 --- /dev/null +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Mail\Test\Unit\Template; + +use Magento\Framework\Mail\Template\TransportBuilderByStore; + +class TransportBuilderByStoreTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Framework\Mail\Template\TransportBuilderByStore + */ + protected $model; + + /** + * @var \Magento\Framework\Mail\Message | \PHPUnit_Framework_MockObject_MockObject + */ + protected $messageMock; + + /** + * @var \Magento\Framework\Mail\Template\SenderResolverInterface | \PHPUnit_Framework_MockObject_MockObject + */ + protected $senderResolverMock; + + /** + * @return void + */ + protected function setUp() + { + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->messageMock = $this->createMock(\Magento\Framework\Mail\Message::class); + $this->senderResolverMock = $this->createMock(\Magento\Framework\Mail\Template\SenderResolverInterface::class); + + $this->model = $objectManagerHelper->getObject( + TransportBuilderByStore::class, + [ + 'message' => $this->messageMock, + 'senderResolver' => $this->senderResolverMock, + ] + ); + } + + /** + * @return void + */ + public function testSetFromByStore() + { + $sender = ['email' => 'from@example.com', 'name' => 'name']; + $store = 1; + $this->senderResolverMock->expects($this->once()) + ->method('resolve') + ->with($sender, $store) + ->willReturn($sender); + $this->messageMock->expects($this->once()) + ->method('setFrom') + ->with('from@example.com', 'name') + ->willReturnSelf(); + + $this->model->setFromByStore($sender, $store); + } +} From 690dd08208b8bbb98f747e31356a72334d1f7f8c Mon Sep 17 00:00:00 2001 From: bshevchenko <1408sheva@gmail.com> Date: Thu, 18 Oct 2018 15:01:37 +0300 Subject: [PATCH 058/704] MAGETWO-95169: Add Bundle product with zero price to shopping cart --- ...rontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml | 2 +- .../Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml index 32d141d7e533e..f263ff85dd737 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -72,7 +72,7 @@ <conditionalClick selector="{{CheckoutPaymentSection.ProductOptionsByProductItemName($$createProduct.name$$)}}" dependentSelector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" visible="false" stepKey="exposeProductOptions"/> <see selector="{{CheckoutPaymentSection.ProductOptionsActiveByProductItemName($$createProduct.name$$)}}" userInput="{{ProductOptionValueDropdownLongTitle1.title}}" stepKey="seeProductOptionValueDropdown1Input1"/> - + <click selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}" stepKey="selectFirstShippingMethod"/> <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> <!-- Place Order --> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml index 9dd2127fa28b2..99ba9cf3fcb67 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerTest.xml @@ -19,6 +19,9 @@ <group value="customer"/> <group value="create"/> </annotations> + <before> + <magentoCLI command="indexer:reindex customer_grid" stepKey="reindexCustomerGrid"/> + </before> <after> <amOnPage url="admin/admin/auth/logout/" stepKey="amOnLogoutPage"/> </after> From f672382b8e18a40163b53b2db60589ed388538b3 Mon Sep 17 00:00:00 2001 From: bshevchenko <1408sheva@gmail.com> Date: Fri, 19 Oct 2018 12:24:28 +0300 Subject: [PATCH 059/704] MAGETWO-95169: Add Bundle product with zero price to shopping cart --- ...StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml index 71aaf76c42e84..33181d6e920eb 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleProductWithZeroPriceToShoppingCartTest.xml @@ -71,6 +71,7 @@ <argument name="orderId" value="$grabOrderNumber"/> </actionGroup> <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <waitForPageLoad stepKey="waitForAdminOrderPageLoad"/> <scrollTo selector="{{AdminOrderTotalSection.subTotal}}" stepKey="scrollToOrderTotalSection"/> <see selector="{{AdminOrderTotalSection.subTotal}}" userInput="$0.00" stepKey="checkSubtotal"/> </test> From c32ca7a0d58cbc57276349b26eb5ecd38451fd2d Mon Sep 17 00:00:00 2001 From: gwharton <30697781+gwharton@users.noreply.github.com> Date: Sat, 20 Oct 2018 14:13:29 +0100 Subject: [PATCH 060/704] Fixed Whitespace issue --- .../Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php index 86d65cc476668..759d60d9e6613 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/SenderBuilderTest.php @@ -122,7 +122,7 @@ public function testSend() $customerName = 'test_name'; $customerEmail = 'test_email'; $identity = 'email_identity_test'; - + $transportMock = $this->createMock( \Magento\Sales\Test\Unit\Model\Order\Email\Stub\TransportInterfaceMock::class ); From 9e7f192135dd4af674c2dbc72348399c07d48a21 Mon Sep 17 00:00:00 2001 From: Oleksii Korshenko <korshenk@adobe.com> Date: Thu, 25 Oct 2018 18:11:06 -0500 Subject: [PATCH 061/704] Added allure report to the unit tests --- composer.json | 3 +- composer.lock | 320 ++++++++++++++++++++------------ dev/tests/unit/phpunit.xml.dist | 6 + 3 files changed, 214 insertions(+), 115 deletions(-) diff --git a/composer.json b/composer.json index 3f8f0a033c893..b6371bf1acb65 100644 --- a/composer.json +++ b/composer.json @@ -89,7 +89,8 @@ "phpmd/phpmd": "@stable", "phpunit/phpunit": "~6.5.0", "sebastian/phpcpd": "~3.0.0", - "squizlabs/php_codesniffer": "3.3.1" + "squizlabs/php_codesniffer": "3.3.1", + "allure-framework/allure-phpunit": "~1.2.0" }, "suggest": { "ext-pcntl": "Need for run processes in parallel mode" diff --git a/composer.lock b/composer.lock index 1d101c8aaaf15..8acc4500db574 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d6640ddfd342feceaec44c406c056f57", + "content-hash": "ab6b79c54f3685e6dce2c170e8fe6f26", "packages": [ { "name": "braintree/braintree_php", @@ -201,16 +201,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.1.2", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "46afded9720f40b9dc63542af4e3e43a1177acb0" + "reference": "8afa52cd417f4ec417b4bfe86b68106538a87660" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/46afded9720f40b9dc63542af4e3e43a1177acb0", - "reference": "46afded9720f40b9dc63542af4e3e43a1177acb0", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/8afa52cd417f4ec417b4bfe86b68106538a87660", + "reference": "8afa52cd417f4ec417b4bfe86b68106538a87660", "shasum": "" }, "require": { @@ -253,7 +253,7 @@ "ssl", "tls" ], - "time": "2018-08-08T08:57:40+00:00" + "time": "2018-10-18T06:09:13+00:00" }, { "name": "composer/composer", @@ -1104,16 +1104,16 @@ }, { "name": "paragonie/sodium_compat", - "version": "v1.6.4", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/paragonie/sodium_compat.git", - "reference": "3f2fd07977541b4d630ea0365ad0eceddee5179c" + "reference": "7b73005be3c224f12c47bd75a23ce24b762e47e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/3f2fd07977541b4d630ea0365ad0eceddee5179c", - "reference": "3f2fd07977541b4d630ea0365ad0eceddee5179c", + "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/7b73005be3c224f12c47bd75a23ce24b762e47e8", + "reference": "7b73005be3c224f12c47bd75a23ce24b762e47e8", "shasum": "" }, "require": { @@ -1182,7 +1182,7 @@ "secret-key cryptography", "side-channel resistant" ], - "time": "2018-08-29T22:02:48+00:00" + "time": "2018-09-22T03:59:58+00:00" }, { "name": "pelago/emogrifier", @@ -1255,16 +1255,16 @@ }, { "name": "php-amqplib/php-amqplib", - "version": "v2.7.2", + "version": "v2.7.3", "source": { "type": "git", "url": "https://github.com/php-amqplib/php-amqplib.git", - "reference": "dfd3694a86f1a7394d3693485259d4074a6ec79b" + "reference": "a8ba54bd35b973fc6861e4c2e105f71e9e95f43f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/dfd3694a86f1a7394d3693485259d4074a6ec79b", - "reference": "dfd3694a86f1a7394d3693485259d4074a6ec79b", + "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/a8ba54bd35b973fc6861e4c2e105f71e9e95f43f", + "reference": "a8ba54bd35b973fc6861e4c2e105f71e9e95f43f", "shasum": "" }, "require": { @@ -1322,7 +1322,7 @@ "queue", "rabbitmq" ], - "time": "2018-02-11T19:28:00+00:00" + "time": "2018-04-30T03:54:54+00:00" }, { "name": "phpseclib/mcrypt_compat", @@ -1834,16 +1834,16 @@ }, { "name": "symfony/console", - "version": "v4.1.4", + "version": "v4.1.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "ca80b8ced97cf07390078b29773dc384c39eee1f" + "reference": "dc7122fe5f6113cfaba3b3de575d31112c9aa60b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/ca80b8ced97cf07390078b29773dc384c39eee1f", - "reference": "ca80b8ced97cf07390078b29773dc384c39eee1f", + "url": "https://api.github.com/repos/symfony/console/zipball/dc7122fe5f6113cfaba3b3de575d31112c9aa60b", + "reference": "dc7122fe5f6113cfaba3b3de575d31112c9aa60b", "shasum": "" }, "require": { @@ -1898,11 +1898,11 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:24:31+00:00" + "time": "2018-10-03T08:15:46+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.1.4", + "version": "v4.1.6", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -1965,16 +1965,16 @@ }, { "name": "symfony/filesystem", - "version": "v4.1.4", + "version": "v4.1.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "c0f5f62db218fa72195b8b8700e4b9b9cf52eb5e" + "reference": "596d12b40624055c300c8b619755b748ca5cf0b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/c0f5f62db218fa72195b8b8700e4b9b9cf52eb5e", - "reference": "c0f5f62db218fa72195b8b8700e4b9b9cf52eb5e", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/596d12b40624055c300c8b619755b748ca5cf0b5", + "reference": "596d12b40624055c300c8b619755b748ca5cf0b5", "shasum": "" }, "require": { @@ -2011,20 +2011,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-08-18T16:52:46+00:00" + "time": "2018-10-02T12:40:59+00:00" }, { "name": "symfony/finder", - "version": "v4.1.4", + "version": "v4.1.6", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "e162f1df3102d0b7472805a5a9d5db9fcf0a8068" + "reference": "1f17195b44543017a9c9b2d437c670627e96ad06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/e162f1df3102d0b7472805a5a9d5db9fcf0a8068", - "reference": "e162f1df3102d0b7472805a5a9d5db9fcf0a8068", + "url": "https://api.github.com/repos/symfony/finder/zipball/1f17195b44543017a9c9b2d437c670627e96ad06", + "reference": "1f17195b44543017a9c9b2d437c670627e96ad06", "shasum": "" }, "require": { @@ -2060,7 +2060,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:24:31+00:00" + "time": "2018-10-03T08:47:56+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2181,16 +2181,16 @@ }, { "name": "symfony/process", - "version": "v4.1.4", + "version": "v4.1.6", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "86cdb930a6a855b0ab35fb60c1504cb36184f843" + "reference": "ee33c0322a8fee0855afcc11fff81e6b1011b529" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/86cdb930a6a855b0ab35fb60c1504cb36184f843", - "reference": "86cdb930a6a855b0ab35fb60c1504cb36184f843", + "url": "https://api.github.com/repos/symfony/process/zipball/ee33c0322a8fee0855afcc11fff81e6b1011b529", + "reference": "ee33c0322a8fee0855afcc11fff81e6b1011b529", "shasum": "" }, "require": { @@ -2226,20 +2226,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-08-03T11:13:38+00:00" + "time": "2018-10-02T12:40:59+00:00" }, { "name": "tedivm/jshrink", - "version": "v1.3.0", + "version": "v1.3.1", "source": { "type": "git", "url": "https://github.com/tedious/JShrink.git", - "reference": "68ce379b213741e86f02bf6053b0d26b9f833448" + "reference": "21254058dc3ce6aba6bef458cff4bfa25cf8b198" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tedious/JShrink/zipball/68ce379b213741e86f02bf6053b0d26b9f833448", - "reference": "68ce379b213741e86f02bf6053b0d26b9f833448", + "url": "https://api.github.com/repos/tedious/JShrink/zipball/21254058dc3ce6aba6bef458cff4bfa25cf8b198", + "reference": "21254058dc3ce6aba6bef458cff4bfa25cf8b198", "shasum": "" }, "require": { @@ -2272,7 +2272,7 @@ "javascript", "minifier" ], - "time": "2017-12-08T00:59:56+00:00" + "time": "2018-09-16T00:02:51+00:00" }, { "name": "true/punycode", @@ -4616,6 +4616,56 @@ ], "time": "2016-12-07T12:15:46+00:00" }, + { + "name": "allure-framework/allure-phpunit", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/allure-framework/allure-phpunit.git", + "reference": "45504aeba41304cf155a898fa9ac1aae79f4a089" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/allure-framework/allure-phpunit/zipball/45504aeba41304cf155a898fa9ac1aae79f4a089", + "reference": "45504aeba41304cf155a898fa9ac1aae79f4a089", + "shasum": "" + }, + "require": { + "allure-framework/allure-php-api": "~1.1.0", + "mikey179/vfsstream": "1.*", + "php": ">=7.0.0", + "phpunit/phpunit": ">=6.0.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Yandex": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Ivan Krutov", + "email": "vania-pooh@yandex-team.ru", + "role": "Developer" + } + ], + "description": "A PHPUnit adapter for Allure report.", + "homepage": "http://allure.qatools.ru/", + "keywords": [ + "allure", + "attachments", + "cases", + "phpunit", + "report", + "steps", + "testing" + ], + "time": "2017-11-03T13:08:21+00:00" + }, { "name": "behat/gherkin", "version": "v4.4.5", @@ -4804,16 +4854,16 @@ }, { "name": "consolidation/annotated-command", - "version": "2.8.5", + "version": "2.9.1", "source": { "type": "git", "url": "https://github.com/consolidation/annotated-command.git", - "reference": "1e8ff512072422b850b44aa721b5b303e4a5ebb3" + "reference": "4bdbb8fa149e1cc1511bd77b0bc4729fd66bccac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/1e8ff512072422b850b44aa721b5b303e4a5ebb3", - "reference": "1e8ff512072422b850b44aa721b5b303e4a5ebb3", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/4bdbb8fa149e1cc1511bd77b0bc4729fd66bccac", + "reference": "4bdbb8fa149e1cc1511bd77b0bc4729fd66bccac", "shasum": "" }, "require": { @@ -4852,20 +4902,20 @@ } ], "description": "Initialize Symfony Console commands from annotated command class methods.", - "time": "2018-08-18T23:51:49+00:00" + "time": "2018-09-19T17:47:18+00:00" }, { "name": "consolidation/config", - "version": "1.1.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/consolidation/config.git", - "reference": "c9fc25e9088a708637e18a256321addc0670e578" + "reference": "925231dfff32f05b787e1fddb265e789b939cf4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/config/zipball/c9fc25e9088a708637e18a256321addc0670e578", - "reference": "c9fc25e9088a708637e18a256321addc0670e578", + "url": "https://api.github.com/repos/consolidation/config/zipball/925231dfff32f05b787e1fddb265e789b939cf4c", + "reference": "925231dfff32f05b787e1fddb265e789b939cf4c", "shasum": "" }, "require": { @@ -4906,7 +4956,7 @@ } ], "description": "Provide configuration services for a commandline tool.", - "time": "2018-08-07T22:57:00+00:00" + "time": "2018-10-24T17:55:35+00:00" }, { "name": "consolidation/log", @@ -4959,19 +5009,20 @@ }, { "name": "consolidation/output-formatters", - "version": "3.2.1", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/consolidation/output-formatters.git", - "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5" + "reference": "a942680232094c4a5b21c0b7e54c20cce623ae19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/d78ef59aea19d3e2e5a23f90a055155ee78a0ad5", - "reference": "d78ef59aea19d3e2e5a23f90a055155ee78a0ad5", + "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/a942680232094c4a5b21c0b7e54c20cce623ae19", + "reference": "a942680232094c4a5b21c0b7e54c20cce623ae19", "shasum": "" }, "require": { + "dflydev/dot-access-data": "^1.1.0", "php": ">=5.4.0", "symfony/console": "^2.8|^3|^4", "symfony/finder": "^2.5|^3|^4" @@ -5010,7 +5061,7 @@ } ], "description": "Format text by applying transformations provided by plug-in formatters.", - "time": "2018-05-25T18:02:34+00:00" + "time": "2018-10-19T22:35:38+00:00" }, { "name": "consolidation/robo", @@ -5095,16 +5146,16 @@ }, { "name": "consolidation/self-update", - "version": "1.1.3", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/consolidation/self-update.git", - "reference": "de33822f907e0beb0ffad24cf4b1b4fae5ada318" + "reference": "4422e52d3fabeca9129ecb1780f198f202debdce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consolidation/self-update/zipball/de33822f907e0beb0ffad24cf4b1b4fae5ada318", - "reference": "de33822f907e0beb0ffad24cf4b1b4fae5ada318", + "url": "https://api.github.com/repos/consolidation/self-update/zipball/4422e52d3fabeca9129ecb1780f198f202debdce", + "reference": "4422e52d3fabeca9129ecb1780f198f202debdce", "shasum": "" }, "require": { @@ -5141,7 +5192,7 @@ } ], "description": "Provides a self:update command for Symfony Console applications.", - "time": "2018-08-24T17:01:46+00:00" + "time": "2018-10-21T20:17:55+00:00" }, { "name": "dflydev/dot-access-data", @@ -5594,16 +5645,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v2.13.0", + "version": "v2.13.1", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "7136aa4e0c5f912e8af82383775460d906168a10" + "reference": "54814c62d5beef3ba55297b9b3186ed8b8a1b161" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/7136aa4e0c5f912e8af82383775460d906168a10", - "reference": "7136aa4e0c5f912e8af82383775460d906168a10", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/54814c62d5beef3ba55297b9b3186ed8b8a1b161", + "reference": "54814c62d5beef3ba55297b9b3186ed8b8a1b161", "shasum": "" }, "require": { @@ -5614,7 +5665,7 @@ "ext-tokenizer": "*", "php": "^5.6 || >=7.0 <7.3", "php-cs-fixer/diff": "^1.3", - "symfony/console": "^3.2 || ^4.0", + "symfony/console": "^3.4.17 || ^4.1.6", "symfony/event-dispatcher": "^3.0 || ^4.0", "symfony/filesystem": "^3.0 || ^4.0", "symfony/finder": "^3.0 || ^4.0", @@ -5650,11 +5701,6 @@ "php-cs-fixer" ], "type": "application", - "extra": { - "branch-alias": { - "dev-master": "2.13-dev" - } - }, "autoload": { "psr-4": { "PhpCsFixer\\": "src/" @@ -5686,7 +5732,7 @@ } ], "description": "A tool to automatically fix PHP code style", - "time": "2018-08-23T13:15:44+00:00" + "time": "2018-10-21T00:32:10+00:00" }, { "name": "fzaninotto/faker", @@ -6420,6 +6466,52 @@ ], "time": "2018-09-05T15:17:20+00:00" }, + { + "name": "mikey179/vfsStream", + "version": "v1.6.5", + "source": { + "type": "git", + "url": "https://github.com/mikey179/vfsStream.git", + "reference": "d5fec95f541d4d71c4823bb5e30cf9b9e5b96145" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mikey179/vfsStream/zipball/d5fec95f541d4d71c4823bb5e30cf9b9e5b96145", + "reference": "d5fec95f541d4d71c4823bb5e30cf9b9e5b96145", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "org\\bovigo\\vfs\\": "src/main/php" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Frank Kleine", + "homepage": "http://frankkleine.de/", + "role": "Developer" + } + ], + "description": "Virtual file system to mock the real file system in unit tests.", + "homepage": "http://vfs.bovigo.org/", + "time": "2017-08-01T08:02:14+00:00" + }, { "name": "moontoast/math", "version": "1.1.2", @@ -8228,7 +8320,7 @@ }, { "name": "symfony/browser-kit", - "version": "v4.1.4", + "version": "v4.1.6", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", @@ -8285,16 +8377,16 @@ }, { "name": "symfony/config", - "version": "v4.1.4", + "version": "v4.1.6", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "76015a3cc372b14d00040ff58e18e29f69eba717" + "reference": "b3d4d7b567d7a49e6dfafb6d4760abc921177c96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/76015a3cc372b14d00040ff58e18e29f69eba717", - "reference": "76015a3cc372b14d00040ff58e18e29f69eba717", + "url": "https://api.github.com/repos/symfony/config/zipball/b3d4d7b567d7a49e6dfafb6d4760abc921177c96", + "reference": "b3d4d7b567d7a49e6dfafb6d4760abc921177c96", "shasum": "" }, "require": { @@ -8344,20 +8436,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-08-08T06:37:38+00:00" + "time": "2018-09-08T13:24:10+00:00" }, { "name": "symfony/css-selector", - "version": "v4.1.4", + "version": "v4.1.6", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "2a4df7618f869b456f9096781e78c57b509d76c7" + "reference": "d67de79a70a27d93c92c47f37ece958bf8de4d8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/2a4df7618f869b456f9096781e78c57b509d76c7", - "reference": "2a4df7618f869b456f9096781e78c57b509d76c7", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/d67de79a70a27d93c92c47f37ece958bf8de4d8a", + "reference": "d67de79a70a27d93c92c47f37ece958bf8de4d8a", "shasum": "" }, "require": { @@ -8397,20 +8489,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2018-07-26T09:10:45+00:00" + "time": "2018-10-02T16:36:10+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.1.4", + "version": "v4.1.6", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "bae4983003c9d451e278504d7d9b9d7fc1846873" + "reference": "f6b9d893ad28aefd8942dc0469c8397e2216fe30" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/bae4983003c9d451e278504d7d9b9d7fc1846873", - "reference": "bae4983003c9d451e278504d7d9b9d7fc1846873", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f6b9d893ad28aefd8942dc0469c8397e2216fe30", + "reference": "f6b9d893ad28aefd8942dc0469c8397e2216fe30", "shasum": "" }, "require": { @@ -8468,20 +8560,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2018-08-08T11:48:58+00:00" + "time": "2018-10-02T12:40:59+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.1.4", + "version": "v4.1.6", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "1c4519d257e652404c3aa550207ccd8ada66b38e" + "reference": "80e60271bb288de2a2259662cff125cff4f93f95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/1c4519d257e652404c3aa550207ccd8ada66b38e", - "reference": "1c4519d257e652404c3aa550207ccd8ada66b38e", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/80e60271bb288de2a2259662cff125cff4f93f95", + "reference": "80e60271bb288de2a2259662cff125cff4f93f95", "shasum": "" }, "require": { @@ -8525,20 +8617,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:00:49+00:00" + "time": "2018-10-02T12:40:59+00:00" }, { "name": "symfony/http-foundation", - "version": "v4.1.4", + "version": "v4.1.6", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "3a5c91e133b220bb882b3cd773ba91bf39989345" + "reference": "d528136617ff24f530e70df9605acc1b788b08d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3a5c91e133b220bb882b3cd773ba91bf39989345", - "reference": "3a5c91e133b220bb882b3cd773ba91bf39989345", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/d528136617ff24f530e70df9605acc1b788b08d4", + "reference": "d528136617ff24f530e70df9605acc1b788b08d4", "shasum": "" }, "require": { @@ -8579,20 +8671,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2018-08-27T17:47:02+00:00" + "time": "2018-10-03T08:48:45+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.1.4", + "version": "v4.1.6", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "1913f1962477cdbb13df951f8147d5da1fe2412c" + "reference": "40f0e40d37c1c8a762334618dea597d64bbb75ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/1913f1962477cdbb13df951f8147d5da1fe2412c", - "reference": "1913f1962477cdbb13df951f8147d5da1fe2412c", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/40f0e40d37c1c8a762334618dea597d64bbb75ff", + "reference": "40f0e40d37c1c8a762334618dea597d64bbb75ff", "shasum": "" }, "require": { @@ -8633,7 +8725,7 @@ "configuration", "options" ], - "time": "2018-07-26T08:55:25+00:00" + "time": "2018-09-18T12:45:12+00:00" }, { "name": "symfony/polyfill-php70", @@ -8751,16 +8843,16 @@ }, { "name": "symfony/stopwatch", - "version": "v4.1.4", + "version": "v4.1.6", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "966c982df3cca41324253dc0c7ffe76b6076b705" + "reference": "5bfc064125b73ff81229e19381ce1c34d3416f4b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/966c982df3cca41324253dc0c7ffe76b6076b705", - "reference": "966c982df3cca41324253dc0c7ffe76b6076b705", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5bfc064125b73ff81229e19381ce1c34d3416f4b", + "reference": "5bfc064125b73ff81229e19381ce1c34d3416f4b", "shasum": "" }, "require": { @@ -8796,20 +8888,20 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2018-07-26T11:00:49+00:00" + "time": "2018-10-02T12:40:59+00:00" }, { "name": "symfony/yaml", - "version": "v3.4.15", + "version": "v3.4.17", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "c2f4812ead9f847cb69e90917ca7502e6892d6b8" + "reference": "640b6c27fed4066d64b64d5903a86043f4a4de7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/c2f4812ead9f847cb69e90917ca7502e6892d6b8", - "reference": "c2f4812ead9f847cb69e90917ca7502e6892d6b8", + "url": "https://api.github.com/repos/symfony/yaml/zipball/640b6c27fed4066d64b64d5903a86043f4a4de7f", + "reference": "640b6c27fed4066d64b64d5903a86043f4a4de7f", "shasum": "" }, "require": { @@ -8855,7 +8947,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-08-10T07:34:36+00:00" + "time": "2018-10-02T16:33:53+00:00" }, { "name": "theseer/fdomdocument", diff --git a/dev/tests/unit/phpunit.xml.dist b/dev/tests/unit/phpunit.xml.dist index d35f7fc1819fb..ad8406ee92c93 100644 --- a/dev/tests/unit/phpunit.xml.dist +++ b/dev/tests/unit/phpunit.xml.dist @@ -40,6 +40,12 @@ </whitelist> </filter> <listeners> + <listener class="Yandex\Allure\Adapter\AllureAdapter" file="../../../vendor/allure-framework/allure-phpunit/src/Yandex/Allure/Adapter/AllureAdapter.php"> + <arguments> + <string>var/allure-results</string> <!-- XML files output directory --> + <boolean>true</boolean> <!-- Whether to delete previous results on rerun --> + </arguments> + </listener> <listener class="Magento\Framework\TestFramework\Unit\Listener\ReplaceObjectManager"/> </listeners> <logging> From c06e0de13a1844cd5f1ef28da2ba069eed25d272 Mon Sep 17 00:00:00 2001 From: Lusine <lusine_papyan@epam.com> Date: Fri, 26 Oct 2018 14:38:36 +0400 Subject: [PATCH 062/704] MAGETWO-95812: Required RMA Attributes are not validated while Customer submits a return - Add automated test --- .../ActionGroup/OrderAndReturnActionGroup.xml | 36 +++++++ .../SalesEnableRMAStorefrontConfigData.xml | 23 +++++ .../Metadata/sales_enable_rma_config-meta.xml | 20 ++++ .../Page/StorefrontCreateNewReturnPage.xml | 14 +++ .../Page/StorefrontOrderInformationPage.xml | 14 +++ .../Page/StorefrontOrdersAndReturnsPage.xml | 15 +++ .../Section/CreateNewReturnMainSection.xml | 18 ++++ .../OrderAndReturnInformationSection.xml | 20 ++++ .../Section/OrderInformationMainSection.xml | 15 +++ ...lidationWhileCustomerSubmitsReturnTest.xml | 99 +++++++++++++++++++ 10 files changed, 274 insertions(+) create mode 100644 app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderAndReturnActionGroup.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Data/SalesEnableRMAStorefrontConfigData.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Metadata/sales_enable_rma_config-meta.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Page/StorefrontCreateNewReturnPage.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrderInformationPage.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrdersAndReturnsPage.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Section/CreateNewReturnMainSection.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Section/OrderAndReturnInformationSection.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Section/OrderInformationMainSection.xml create mode 100644 app/code/Magento/Sales/Test/Mftf/Test/RequiredRMAAttributesValidationWhileCustomerSubmitsReturnTest.xml diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderAndReturnActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderAndReturnActionGroup.xml new file mode 100644 index 0000000000000..69f6637301bd0 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/OrderAndReturnActionGroup.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!--Fill order information fields and click continue--> + <actionGroup name="fillOrderInformationActionGroup"> + <arguments> + <argument name="orderId" type="string"/> + <argument name="orderLastName"/> + <argument name="orderEmail"/> + </arguments> + <amOnPage url="{{StorefrontOrdersAndReturnsPage.url}}" stepKey="navigateToOrderAndReturnPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <fillField selector="{{OrderAndReturnInformationSection.orderId}}" userInput="{{orderId}}" stepKey="fillOrderId"/> + <fillField selector="{{OrderAndReturnInformationSection.bilingLastName}}" userInput="{{orderLastName}}" stepKey="fillBillingLastName"/> + <fillField selector="{{OrderAndReturnInformationSection.email}}" userInput="{{orderEmail}}" stepKey="fillEmail"/> + <click selector="{{OrderAndReturnInformationSection.continueButton}}" stepKey="clickContinue"/> + <waitForPageLoad stepKey="waitForOrderInformationPageLoad"/> + <seeInCurrentUrl url="{{StorefrontOrderInformationPage.url}}" stepKey="seeOrderInformationUrl"/> + </actionGroup> + + <!--Enter quantity to return and submit--> + <actionGroup name="fillQuantityToReturnActionGroup"> + <click selector="{{OrderInformationMainSection.return}}" stepKey="gotToCreateNewReturnPage"/> + <waitForPageLoad stepKey="waitForReturnPageLoad"/> + <fillField selector="{{CreateNewReturnMainSection.quantityToReturn}}" userInput="1" stepKey="fillQuantityToReturn"/> + <click selector="{{CreateNewReturnMainSection.submit}}" stepKey="clickSubmit"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Data/SalesEnableRMAStorefrontConfigData.xml b/app/code/Magento/Sales/Test/Mftf/Data/SalesEnableRMAStorefrontConfigData.xml new file mode 100644 index 0000000000000..76ff20813483e --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Data/SalesEnableRMAStorefrontConfigData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="EnableRMA" type="sales_rma_config"> + <requiredEntity type="enabled">EnableRMAStorefront</requiredEntity> + </entity> + <entity name="EnableRMAStorefront" type="enabled"> + <data key="value">1</data> + </entity> + + <entity name="DisableRMA" type="sales_rma_config"> + <requiredEntity type="enabled">DisableRMAStorefront</requiredEntity> + </entity> + <entity name="DisableRMAStorefront" type="enabled"> + <data key="value">0</data> + </entity> +</entities> diff --git a/app/code/Magento/Sales/Test/Mftf/Metadata/sales_enable_rma_config-meta.xml b/app/code/Magento/Sales/Test/Mftf/Metadata/sales_enable_rma_config-meta.xml new file mode 100644 index 0000000000000..3c63fc0a63e8b --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Metadata/sales_enable_rma_config-meta.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> + <operation name="SalesRMAConfig" dataType="sales_rma_config" type="create" auth="adminFormKey" url="/admin/system_config/save/section/sales/" method="POST"> + <object key="groups" dataType="sales_rma_config"> + <object key="magento_rma" dataType="sales_rma_config"> + <object key="fields" dataType="sales_rma_config"> + <object key="enabled" dataType="enabled"> + <field key="value">string</field> + </object> + </object> + </object> + </object> + </operation> +</operations> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontCreateNewReturnPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontCreateNewReturnPage.xml new file mode 100644 index 0000000000000..f578626156fee --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontCreateNewReturnPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="StorefrontCreateNewReturnPage" url="rma/guest/create/order_id/" area="guest" module="Magento_Sales"> + <section name="CreateNewReturnMainSection"/> + </page> +</pages> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrderInformationPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrderInformationPage.xml new file mode 100644 index 0000000000000..e18a80eb45fb1 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrderInformationPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="StorefrontOrderInformationPage" url="sales/guest/view" area="guest" module="Magento_Sales"> + <section name="OrderInformationMainSection"/> + </page> +</pages> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrdersAndReturnsPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrdersAndReturnsPage.xml new file mode 100644 index 0000000000000..32d94c3175807 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrdersAndReturnsPage.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="StorefrontOrdersAndReturnsPage" url="sales/guest/form" area="guest" module="Magento_Sales"> + <section name="OrderAndReturnsMainSection"/> + <section name="OrderInformationSection"/> + </page> +</pages> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/CreateNewReturnMainSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/CreateNewReturnMainSection.xml new file mode 100644 index 0000000000000..e1349520e2742 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/CreateNewReturnMainSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="CreateNewReturnMainSection"> + <element name="quantityToReturn" type="input" selector="#items:qty_requested0"/> + <element name="submit" type="submit" selector="//span[contains(text(), 'Submit')]"/> + <element name="resolutionError" type="text" selector="//*[@id='items:resolution0']/following-sibling::div[contains(text(),'Please select an option')]"/> + <element name="conditionError" type="text" selector="//*[@id='items:condition0']/following-sibling::div[contains(text(),'Please select an option')]"/> + <element name="reasonError" type="text" selector="//*[@id='items:reason0']/following-sibling::div[contains(text(),'Please select an option')]"/> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/OrderAndReturnInformationSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/OrderAndReturnInformationSection.xml new file mode 100644 index 0000000000000..65f7f8c701d72 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/OrderAndReturnInformationSection.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="OrderAndReturnInformationSection"> + <element name="orderId" type="input" selector="#oar-order-id"/> + <element name="bilingLastName" type="input" selector="#oar-billing-lastname"/> + <element name="findOrderBy" type="select" selector="#quick-search-type-id"/> + <element name="email" type="input" selector="#oar_email"/> + <element name="bilingZipCode" type="input" selector="//input[@id='oar_zip']"/> + <element name="continueButton" type="submit" selector="//button[@title='Continue']"/> + <element name="ordersAndReturnsTitle" type="span" selector="//span[@id='page-title-wrapper']"/> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/OrderInformationMainSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/OrderInformationMainSection.xml new file mode 100644 index 0000000000000..b5c21db09332d --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/OrderInformationMainSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="OrderInformationMainSection"> + <element name="orderTitle" type="span" selector="#page-title-wrapper"/> + <element name="return" type="span" selector="//span[contains(text(), 'Return')]"/> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/RequiredRMAAttributesValidationWhileCustomerSubmitsReturnTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/RequiredRMAAttributesValidationWhileCustomerSubmitsReturnTest.xml new file mode 100644 index 0000000000000..e3b2352c4c810 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/RequiredRMAAttributesValidationWhileCustomerSubmitsReturnTest.xml @@ -0,0 +1,99 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="RequiredRMAAttributesValidationWhileCustomerSubmitsReturnTest"> + <annotations> + <features value="Sales"/> + <stories value="MAGETWO-95812: Required RMA Attributes are not validated while Customer submits a return"/> + <title value="Required RMA Attributes Validation While Customer Submits A Return"/> + <description value="Required RMA Attributes Validation While Customer Submits A Return"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-95864"/> + <group value="sales"/> + </annotations> + <before> + <!-- log in as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Enabled RMA On Storefront--> + <createData entity="EnableRMA" stepKey="enabledRMAOnStorefront"/> + <!-- create new product --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + + <!-- Place an order from frontend --> + + <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="checkoutProductFromCart"/> + + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection"> + <argument name="customerVar" value="CustomerEntityOne" /> + <argument name="customerAddressVar" value="CustomerAddressSimple" /> + </actionGroup> + + <!-- place for order --> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + <waitForPageLoad stepKey="waitForPlaceOrder"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> + + <!--Complete the order from admin bay creating Invoice and then Shipment--> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrdersIndexPage"/> + <waitForPageLoad stepKey="waitForOrderIndexPage"/> + + <!-- Open Order --> + <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <argument name="orderId" value="$grabOrderNumber"/> + </actionGroup> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <waitForPageLoad stepKey="waitForCreatedOrderPageOpened"/> + + <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceAction"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seePageNameNewInvoicePage"/> + <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> + + <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> + <seeInCurrentUrl url="{{AdminShipmentNewPage.url}}" stepKey="seeOrderShipmentUrl"/> + <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment"/> + + <!--Goes to Orders and Returns --> + <amOnPage url="{{StorefrontOrdersAndReturnsPage.url}}" stepKey="goToOrderAndReturnPage"/> + <waitForPageLoad stepKey="waitForOrdersAndReturnsPageLoad"/> + <seeInCurrentUrl url="{{StorefrontOrdersAndReturnsPage.url}}" stepKey="seeOrdersAndReturnsUrl"/> + + <actionGroup ref="fillOrderInformationActionGroup" stepKey="fillOrderInformationAndContinue"> + <argument name="orderId" value="$grabOrderNumber"/> + <argument name="orderLastName" value="CustomerEntityOne.lastname"/> + <argument name="orderEmail" value="CustomerEntityOne.email"/> + </actionGroup> + + <click selector="{{OrderInformationMainSection.return}}" stepKey="clickOnReturn"/> + <actionGroup ref="fillQuantityToReturnActionGroup" stepKey="fillQuantityToReturn"/> + + <seeElement selector="{{CreateNewReturnMainSection.resolutionError}}" stepKey="AssertResolutionError" /> + <seeElement selector="{{CreateNewReturnMainSection.conditionError}}" stepKey="AssertConditionError" /> + <seeElement selector="{{CreateNewReturnMainSection.reasonError}}" stepKey="AssertReasonError" /> + + <after> + <!--Disable RMA On Storefront--> + <createData entity="DisableRMA" stepKey="disableRMAOnStorefront"/> + + <!--Delete the created product--> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + </test> +</tests> From b25c01f70567e86ab3d2a6ed63faa37362b8ab00 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Fri, 26 Oct 2018 16:12:59 +0300 Subject: [PATCH 063/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page --- .../Adminhtml/Edit/Address/CancelButton.php | 41 + .../Adminhtml/Edit/Address/DeleteButton.php | 52 + .../Adminhtml/Edit/Address/GenericButton.php | 110 ++ .../Adminhtml/Edit/Address/SaveButton.php | 33 + .../Address/AbstractDefaultAddress.php | 99 ++ .../Address/DefaultBillingAddress.php | 39 + .../Address/DefaultShippingAddress.php | 39 + .../Controller/Adminhtml/Address/Delete.php | 65 + .../Adminhtml/Address/MassDelete.php | 87 ++ .../Controller/Adminhtml/Address/Save.php | 149 +++ .../Controller/Adminhtml/Address/Validate.php | 97 ++ .../Adminhtml/File/Address/Upload.php | 28 +- .../Controller/Adminhtml/Index/Save.php | 21 +- .../Controller/Adminhtml/Index/Validate.php | 39 +- .../Customer/Model/Address/DataProvider.php | 574 +++++++++ .../DataProviderWithDefaultAddresses.php | 585 +++++++++ .../Customer/Model/Metadata/Form/File.php | 2 +- .../ResourceModel/Address/Grid/Collection.php | 171 +++ .../Model/ResourceModel/Address/Relation.php | 15 +- .../ResourceModel/CustomerRepository.php | 54 +- .../Test/Unit/Controller/Address/SaveTest.php | 205 ++++ .../Unit/Controller/Address/ValidateTest.php | 118 ++ .../Controller/Adminhtml/Index/SaveTest.php | 226 +--- .../Adminhtml/Index/ValidateTest.php | 28 - .../Unit/Model/Address/DataProviderTest.php | 233 ++++ .../DataProviderWithDefaultAddressesTest.php | 1065 +++++++++++++++++ .../ResourceModel/CustomerRepositoryTest.php | 126 -- .../Ui/Component/Form/AddressFieldsetTest.php | 69 ++ .../Ui/Component/Form/AddressFieldset.php | 45 + .../Listing/Address/Column/Actions.php | 134 +++ .../Listing/Address/Column/Countries.php | 37 + app/code/Magento/Customer/etc/db_schema.xml | 9 + app/code/Magento/Customer/etc/di.xml | 9 + .../layout/customer_address_edit.xml | 15 + .../ui_component/customer_address_form.xml | 230 ++++ .../ui_component/customer_address_listing.xml | 171 +++ .../web/js/address/default-address-block.js | 17 + .../web/js/address/default-address.js | 44 + .../view/adminhtml/web/js/address/modal.js | 203 ++++ .../web/js/form/components/insert-form.js | 164 +++ .../web/template/default-address.html | 42 + .../view/base/ui_component/customer_form.xml | 357 +++--- .../Component/Form/Element/DataType/Media.php | 1 + .../Magento/Ui/Component/Form/Fieldset.php | 14 +- app/code/Magento/Ui/Component/Layout/Tabs.php | 4 + .../Test/Unit/Component/Form/FieldsetTest.php | 69 ++ .../base/web/js/form/components/collection.js | 7 +- .../web/js/form/components/collection/item.js | 5 +- .../view/base/web/templates/form/insert.html | 3 - .../web/css/source/_module.less | 72 +- .../Controller/Adminhtml/Address/SaveTest.php | 222 ++++ .../Test/Php/_files/blacklist/strict_type.txt | 2 +- .../config/customerConfig.xml | 2 +- 53 files changed, 5529 insertions(+), 719 deletions(-) create mode 100644 app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php create mode 100644 app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php create mode 100644 app/code/Magento/Customer/Block/Adminhtml/Edit/Address/GenericButton.php create mode 100644 app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php create mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php create mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php create mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php create mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php create mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php create mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php create mode 100644 app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php create mode 100644 app/code/Magento/Customer/Model/Address/DataProvider.php create mode 100644 app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php create mode 100644 app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php create mode 100644 app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php create mode 100644 app/code/Magento/Customer/Test/Unit/Controller/Address/ValidateTest.php create mode 100644 app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php create mode 100644 app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php create mode 100644 app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php create mode 100644 app/code/Magento/Customer/Ui/Component/Form/AddressFieldset.php create mode 100644 app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php create mode 100644 app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php create mode 100644 app/code/Magento/Customer/view/adminhtml/layout/customer_address_edit.xml create mode 100644 app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml create mode 100644 app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/address/default-address-block.js create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/address/modal.js create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js create mode 100644 app/code/Magento/Customer/view/adminhtml/web/template/default-address.html create mode 100644 app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php new file mode 100644 index 0000000000000..80d9780f819d0 --- /dev/null +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Block\Adminhtml\Edit\Address; + +use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface; +use Magento\Customer\Block\Adminhtml\Edit\GenericButton; + +/** + * Class CancelButton + */ +class CancelButton extends GenericButton implements ButtonProviderInterface +{ + /** + * {@inheritdoc} + * + * @return array + */ + public function getButtonData() + { + return [ + 'label' => __('Cancel'), + 'on_click' => '', + 'data_attribute' => [ + 'mage-init' => [ + 'Magento_Ui/js/form/button-adapter' => [ + 'actions' => [ + [ + 'targetName' => 'customer_form.areas.address.address.customer_address_update_modal', + 'actionName' => 'closeModal' + ], + ], + ], + ], + ], + 'sort_order' => 20 + ]; + } +} diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php new file mode 100644 index 0000000000000..8375aa13fdeff --- /dev/null +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php @@ -0,0 +1,52 @@ +<?php +declare(strict_types=1); +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Block\Adminhtml\Edit\Address; + +use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface; +use Magento\Customer\Ui\Component\Listing\Address\Column\Actions; + +/** + * Delete button on edit customer address form + */ +class DeleteButton extends GenericButton implements ButtonProviderInterface +{ + /** + * Get delete button data. + * + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function getButtonData() + { + $data = []; + if ($this->getAddressId()) { + $data = [ + 'label' => __('Delete'), + 'class' => 'delete', + 'on_click' => 'deleteConfirm(\'' . __( + 'Are you sure you want to delete this address?' + ) . '\', \'' . $this->getDeleteUrl() . '\')', + 'sort_order' => 15, + ]; + } + return $data; + } + + /** + * Get delete button url. + * + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function getDeleteUrl(): string + { + return $this->getUrl( + Actions::CUSTOMER_ADDRESS_PATH_DELETE, + ['parent_id' => $this->getCustomerId(), 'id' => $this->getAddressId()] + ); + } +} diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/GenericButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/GenericButton.php new file mode 100644 index 0000000000000..ae09ee6896891 --- /dev/null +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/GenericButton.php @@ -0,0 +1,110 @@ +<?php +declare(strict_types=1); +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Block\Adminhtml\Edit\Address; + +use Magento\Customer\Model\AddressFactory; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\UrlInterface; +use Magento\Customer\Model\ResourceModel\Address; +use Magento\Customer\Model\ResourceModel\AddressRepository; + +/** + * Class for common code for buttons on the create/edit address form + */ +class GenericButton +{ + /** + * @var AddressFactory + */ + private $addressFactory; + + /** + * @var UrlInterface + */ + private $urlBuilder; + + /** + * @var Address + */ + private $addressResourceModel; + + /** + * @var RequestInterface + */ + private $request; + + /** + * @var AddressRepository + */ + private $addressRepository; + + /** + * @param AddressFactory $addressFactory + * @param UrlInterface $urlBuilder + * @param Address $addressResourceModel + * @param RequestInterface $request + * @param AddressRepository $addressRepository + */ + public function __construct( + AddressFactory $addressFactory, + UrlInterface $urlBuilder, + Address $addressResourceModel, + RequestInterface $request, + AddressRepository $addressRepository + ) { + $this->addressFactory = $addressFactory; + $this->urlBuilder = $urlBuilder; + $this->addressResourceModel = $addressResourceModel; + $this->request = $request; + $this->addressRepository = $addressRepository; + } + + /** + * Return address Id. + * + * @return int|null + */ + public function getAddressId() + { + $address = $this->addressFactory->create(); + + $entityId = $this->request->getParam('entity_id'); + $this->addressResourceModel->load( + $address, + $entityId + ); + + return $address->getEntityId() ?: null; + } + + /** + * Get customer id. + * + * @return int|null + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function getCustomerId() + { + $addressId = $this->request->getParam('entity_id'); + + $address = $this->addressRepository->getById($addressId); + + return $address->getCustomerId() ?: null; + } + + /** + * Generate url by route and parameters + * + * @param string $route + * @param array $params + * @return string + */ + public function getUrl($route = '', array $params = []): string + { + return $this->urlBuilder->getUrl($route, $params); + } +} diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php new file mode 100644 index 0000000000000..706ef32c9e5a5 --- /dev/null +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Block\Adminhtml\Edit\Address; + +use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface; +use Magento\Customer\Block\Adminhtml\Edit\GenericButton; + +/** + * Class SaveButton + */ +class SaveButton extends GenericButton implements ButtonProviderInterface +{ + /** + * {@inheritdoc} + * + * @return array + */ + public function getButtonData() + { + return [ + 'label' => __('Save'), + 'class' => 'save primary', + 'data_attribute' => [ + 'mage-init' => ['button' => ['event' => 'save']], + 'form-role' => 'save', + ], + 'sort_order' => 10 + ]; + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php new file mode 100644 index 0000000000000..a2f9d12282188 --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php @@ -0,0 +1,99 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Controller\Adminhtml\Address; + +use Magento\Backend\App\Action; +use Magento\Framework\Controller\Result\Redirect; +use Magento\Framework\Phrase; +use Psr\Log\LoggerInterface; + +/** + * Abstract class for customer default addresses changing + */ +abstract class AbstractDefaultAddress extends Action +{ + /** + * Authorization level of a basic admin session + * + * @see _isAllowed() + */ + public const ADMIN_RESOURCE = 'Magento_Customer::manage'; + + /** + * @var \Magento\Customer\Api\AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param Action\Context $context + * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository + * @param LoggerInterface $logger + */ + public function __construct( + Action\Context $context, + \Magento\Customer\Api\AddressRepositoryInterface $addressRepository, + LoggerInterface $logger + ) { + parent::__construct($context); + $this->addressRepository = $addressRepository; + $this->logger = $logger; + } + + /** + * Execute action to change customer default address + * + * @return \Magento\Framework\Controller\Result\Redirect + */ + public function execute(): Redirect + { + $customerId = $this->getRequest()->getParam('parent_id', false); + $addressId = $this->getRequest()->getParam('id', false); + if ($addressId) { + try { + $address = $this->addressRepository->getById($addressId)->setCustomerId($customerId); + $this->setAddressAsDefault($address); + $this->addressRepository->save($address); + + $this->messageManager->addSuccessMessage($this->getSuccessMessage()); + } catch (\Exception $other) { + $this->logger->critical($other); + $this->messageManager->addExceptionMessage($other, $this->getExceptionMessage()); + } + } + /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + $resultRedirect = $this->resultRedirectFactory->create(); + + return $resultRedirect->setPath('customer/index/edit/id', ['id' => $customerId]); + } + + /** + * Set passed address as customer's default address + * + * @param \Magento\Customer\Api\Data\AddressInterface $address + * @return $this + */ + abstract protected function setAddressAsDefault($address); + + /** + * Get success message about default address changed + * + * @return \Magento\Framework\Phrase + */ + abstract protected function getSuccessMessage(): Phrase; + + /** + * Get error message about unsuccessful attempt to change default address + * + * @return \Magento\Framework\Phrase + */ + abstract protected function getExceptionMessage(): Phrase; +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php new file mode 100644 index 0000000000000..bd921a5a6642e --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultBillingAddress.php @@ -0,0 +1,39 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Controller\Adminhtml\Address; + +use Magento\Framework\Phrase; + +/** + * Class to process default billing address setting + */ +class DefaultBillingAddress extends AbstractDefaultAddress +{ + /** + * @inheritdoc + */ + protected function setAddressAsDefault($address) + { + $address->setIsDefaultBilling(true); + } + + /** + * @inheritdoc + */ + protected function getSuccessMessage(): Phrase + { + return __('Default billing address has been changed.'); + } + + /** + * @inheritdoc + */ + protected function getExceptionMessage(): Phrase + { + return __('We can\'t change default billing address right now.'); + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php new file mode 100644 index 0000000000000..7d5bef9ab5be9 --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/DefaultShippingAddress.php @@ -0,0 +1,39 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Controller\Adminhtml\Address; + +use Magento\Framework\Phrase; + +/** + * Class to process default shipping address setting + */ +class DefaultShippingAddress extends AbstractDefaultAddress +{ + /** + * @inheritdoc + */ + protected function setAddressAsDefault($address) + { + $address->setIsDefaultShipping(true); + } + + /** + * @inheritdoc + */ + protected function getSuccessMessage(): Phrase + { + return __('Default shipping address has been changed.'); + } + + /** + * @inheritdoc + */ + protected function getExceptionMessage(): Phrase + { + return __('We can\'t change default shipping address right now.'); + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php new file mode 100644 index 0000000000000..8443c777546f6 --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php @@ -0,0 +1,65 @@ +<?php +/** + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Controller\Adminhtml\Address; + +use Magento\Backend\App\Action; +use Magento\Framework\Controller\Result\Redirect; +use Magento\Customer\Api\AddressRepositoryInterface; + +/** + * Button for deletion of customer address in admin * + */ +class Delete extends Action +{ + /** + * Authorization level of a basic admin session + * + * @see _isAllowed() + */ + public const ADMIN_RESOURCE = 'Magento_Customer::manage'; + + /** + * @var \Magento\Customer\Api\AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @param Action\Context $context + * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository + */ + public function __construct( + Action\Context $context, + AddressRepositoryInterface $addressRepository + ) { + parent::__construct($context); + $this->addressRepository = $addressRepository; + } + + /** + * Delete action + * + * @return \Magento\Framework\Controller\Result\Redirect + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function execute(): Redirect + { + $customerId = $this->getRequest()->getParam('parent_id', false); + $addressId = $this->getRequest()->getParam('id', false); + if ($addressId && $this->addressRepository->getById($addressId)->getCustomerId() === $customerId) { + try { + $this->addressRepository->deleteById($addressId); + $this->messageManager->addSuccessMessage(__('You deleted the address.')); + } catch (\Exception $other) { + $this->messageManager->addExceptionMessage($other, __('We can\'t delete the address right now.')); + } + } + /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + $resultRedirect = $this->resultRedirectFactory->create(); + + return $resultRedirect->setPath('customer/index/edit/id', ['id' => $customerId]); + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php new file mode 100644 index 0000000000000..f022ea36f420d --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Controller\Adminhtml\Address; + +use Magento\Framework\Controller\ResultFactory; +use Magento\Backend\App\Action\Context; +use Magento\Ui\Component\MassAction\Filter; +use Magento\Customer\Model\ResourceModel\Address\CollectionFactory; +use Magento\Backend\Model\View\Result\Redirect; +use Magento\Customer\Api\AddressRepositoryInterface; + +/** + * Class to delete selected customer addresses through massaction + */ +class MassDelete extends \Magento\Backend\App\Action +{ + /** + * Authorization level of a basic admin session + * + * @see MassDelete::_isAllowed() + */ + const ADMIN_RESOURCE = 'Magento_Customer::manage'; + + /** + * @var Filter + */ + protected $filter; + + /** + * @var CollectionFactory + */ + protected $collectionFactory; + + /** + * @var \Magento\Customer\Api\AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @param Context $context + * @param Filter $filter + * @param CollectionFactory $collectionFactory + * @param AddressRepositoryInterface $addressRepository + */ + public function __construct( + Context $context, + Filter $filter, + CollectionFactory $collectionFactory, + AddressRepositoryInterface $addressRepository + ) { + $this->filter = $filter; + $this->collectionFactory = $collectionFactory; + $this->addressRepository = $addressRepository; + parent::__construct($context); + } + + /** + * Execute action + * + * @return \Magento\Backend\Model\View\Result\Redirect + * @throws \Magento\Framework\Exception\LocalizedException|\Exception + */ + public function execute() + { + /** @var \Magento\Customer\Model\ResourceModel\Address\Collection $collection */ + $collection = $this->filter->getCollection($this->collectionFactory->create()); + $collectionSize = $collection->getSize(); + + // Get id of the first item from addresses collection for providing it to the ResultRedirect and build a + // proper redirect URL + $customerId = $collection->getFirstItem()->getParentId(); + + /** @var \Magento\Customer\Model\Address $address */ + foreach ($collection as $address) { + $this->addressRepository->deleteById($address->getId()); + } + $this->messageManager->addSuccessMessage(__('A total of %1 record(s) have been deleted.', $collectionSize)); + + /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); + + return $resultRedirect->setPath('customer/index/edit/id', ['id' => $customerId]); + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php new file mode 100644 index 0000000000000..df041ac4e1202 --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php @@ -0,0 +1,149 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Controller\Adminhtml\Address; + +use Magento\Backend\App\Action; +use Magento\Framework\Controller\Result\Redirect; +use Psr\Log\LoggerInterface; + +/** + * Class for saving of customer address + */ +class Save extends Action +{ + /** + * Authorization level of a basic admin session + * + * @see _isAllowed() + */ + public const ADMIN_RESOURCE = 'Magento_Customer::manage'; + + /** + * @var \Magento\Customer\Api\AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @var \Magento\Customer\Model\Metadata\FormFactory + */ + private $formFactory; + + /** + * @var \Magento\Customer\Api\CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var \Magento\Framework\Api\DataObjectHelper + */ + private $dataObjectHelper; + + /** + * @var \Magento\Customer\Api\Data\AddressInterfaceFactory + */ + private $addressDataFactory; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param Action\Context $context + * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository + * @param \Magento\Customer\Model\Metadata\FormFactory $formFactory + * @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository + * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper + * @param \Magento\Customer\Api\Data\AddressInterfaceFactory $addressDataFactory + * @param LoggerInterface $logger + */ + public function __construct( + Action\Context $context, + \Magento\Customer\Api\AddressRepositoryInterface $addressRepository, + \Magento\Customer\Model\Metadata\FormFactory $formFactory, + \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository, + \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, + \Magento\Customer\Api\Data\AddressInterfaceFactory $addressDataFactory, + LoggerInterface $logger + ) { + parent::__construct($context); + $this->addressRepository = $addressRepository; + $this->formFactory = $formFactory; + $this->customerRepository = $customerRepository; + $this->dataObjectHelper = $dataObjectHelper; + $this->addressDataFactory = $addressDataFactory; + $this->logger = $logger; + } + + /** + * Execute action to save customer address + * + * @return \Magento\Framework\Controller\Result\Redirect + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function execute(): Redirect + { + $customerId = $this->getRequest()->getParam('parent_id', false); + $addressId = $this->getRequest()->getParam('entity_id', false); + /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ + $customer = $this->customerRepository->getById($customerId); + + try { + $addressForm = $this->formFactory->create( + 'customer_address', + 'adminhtml_customer_address', + [], + false, + false + ); + $addressData = $addressForm->extractData($this->getRequest()); + $addressData = $addressForm->compactData($addressData); + + $addressData['region'] = [ + 'region' => $addressData['region'] ?? null, + 'region_id' => $addressData['region_id'] ?? null, + ]; + $addressToSave = $this->addressDataFactory->create(); + $this->dataObjectHelper->populateWithArray( + $addressToSave, + $addressData, + \Magento\Customer\Api\Data\AddressInterface::class + ); + $addressToSave->setCustomerId($customer->getId()); + $addressToSave->setIsDefaultBilling( + (bool)$this->getRequest()->getParam('default_billing', false) + ); + $addressToSave->setIsDefaultShipping( + (bool)$this->getRequest()->getParam('default_shipping', false) + ); + if ($addressId) { + $addressToSave->setId($addressId); + $saveMessage = __('Customer address has been updated.'); + } else { + $addressToSave->setId(null); + $saveMessage = __('New customer address has been added.'); + } + + $this->addressRepository->save($addressToSave); + $this->messageManager->addSuccessMessage($saveMessage); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->messageManager->addErrorMessage($e->getMessage()); + $this->logger->critical($e); + } catch (\Exception $e) { + $this->messageManager->addExceptionMessage( + $e, __('We can\'t change customer address right now.') + ); + } + + $resultRedirect = $this->resultRedirectFactory->create(); + $resultRedirect->setPath( + 'customer/index/edit', + ['id' => $customerId, '_current' => true] + ); + return $resultRedirect; + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php new file mode 100644 index 0000000000000..01ce720a20e63 --- /dev/null +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Controller\Adminhtml\Address; + +use Magento\Backend\App\Action; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; + +/** + * Class for validation of customer address form on admin. + */ +class Validate extends \Magento\Backend\App\Action implements HttpPostActionInterface, HttpGetActionInterface +{ + /** + * Authorization level of a basic admin session + * + * @see _isAllowed() + */ + public const ADMIN_RESOURCE = 'Magento_Customer::manage'; + + /** + * @var \Magento\Framework\Controller\Result\JsonFactory + */ + private $resultJsonFactory; + + /** + * @var \Magento\Customer\Model\Metadata\FormFactory + */ + private $formFactory; + + /** + * @param Action\Context $context + * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory + * @param \Magento\Customer\Model\Metadata\FormFactory $formFactory + */ + public function __construct( + Action\Context $context, + \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory, + \Magento\Customer\Model\Metadata\FormFactory $formFactory + ) { + parent::__construct($context); + $this->resultJsonFactory = $resultJsonFactory; + $this->formFactory = $formFactory; + } + + /** + * AJAX customer validation action + * + * @return \Magento\Framework\Controller\Result\Json + */ + public function execute() + { + /** @var \Magento\Framework\DataObject $response */ + $response = new \Magento\Framework\DataObject(); + $response->setError(0); + + /** @var \Magento\Framework\DataObject $validatedResponse */ + $validatedResponse = $this->validateCustomerAddress($response); + $resultJson = $this->resultJsonFactory->create(); + if ($validatedResponse->getError()) { + $validatedResponse->setError(true); + $validatedResponse->setMessages($response->getMessages()); + } + + $resultJson->setData($validatedResponse); + + return $resultJson; + } + + /** + * Customer address validation. + * + * @param \Magento\Framework\DataObject $response + * @return \Magento\Framework\DataObject + */ + private function validateCustomerAddress(\Magento\Framework\DataObject $response) + { + $addressForm = $this->formFactory->create('customer_address', 'adminhtml_customer_address'); + $formData = $addressForm->extractData($this->getRequest()); + + $errors = $addressForm->validateData($formData); + if ($errors !== true) { + $messages = $response->hasMessages() ? $response->getMessages() : []; + foreach ($errors as $error) { + $messages[] = $error; + } + $response->setMessages($messages); + $response->setError(1); + } + + return $response; + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php b/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php index 506eac3230200..be1b1aec7b3a3 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php @@ -14,6 +14,9 @@ use Magento\Framework\Exception\LocalizedException; use Psr\Log\LoggerInterface; +/** + * Uploads files for customer address + */ class Upload extends Action { /** @@ -38,21 +41,29 @@ class Upload extends Action */ private $logger; + /** + * @var string + */ + private $scope; + /** * @param Context $context * @param FileUploaderFactory $fileUploaderFactory * @param AddressMetadataInterface $addressMetadataService * @param LoggerInterface $logger + * @param string $scope */ public function __construct( Context $context, FileUploaderFactory $fileUploaderFactory, AddressMetadataInterface $addressMetadataService, - LoggerInterface $logger + LoggerInterface $logger, + string $scope = 'address' ) { $this->fileUploaderFactory = $fileUploaderFactory; $this->addressMetadataService = $addressMetadataService; $this->logger = $logger; + $this->scope = $scope; parent::__construct($context); } @@ -69,14 +80,14 @@ public function execute() // Must be executed before any operations with $_FILES! $this->convertFilesArray(); - $attributeCode = key($_FILES['address']['name']); + $attributeCode = key($_FILES[$this->scope]['name']); $attributeMetadata = $this->addressMetadataService->getAttributeMetadata($attributeCode); /** @var FileUploader $fileUploader */ $fileUploader = $this->fileUploaderFactory->create([ 'attributeMetadata' => $attributeMetadata, 'entityTypeCode' => AddressMetadataInterface::ENTITY_TYPE_ADDRESS, - 'scope' => 'address', + 'scope' => $this->scope, ]); $errors = $fileUploader->validate(); @@ -114,14 +125,11 @@ public function execute() */ private function convertFilesArray() { - foreach ($_FILES['address'] as $itemKey => $item) { - foreach ($item as $value) { - if (is_array($value)) { - $_FILES['address'][$itemKey] = [ - key($value) => current($value), - ]; - } + foreach ($_FILES as $itemKey => $item) { + foreach ($item as $fieldName => $value) { + $_FILES[$this->scope][$fieldName] = [$itemKey => $value]; } + unset($_FILES[$itemKey]); } } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index 45a7c0182d41c..aed7908337ee1 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -108,6 +108,7 @@ protected function _extractData( /** * Saves default_billing and default_shipping flags for customer address * + * @deprecated must be removed because addresses are save separately for now * @param array $addressIdList * @param array $extractedCustomerData * @return array @@ -150,6 +151,7 @@ protected function saveDefaultFlags(array $addressIdList, array & $extractedCust /** * Reformat customer addresses data to be compatible with customer service interface * + * @deprecated must be removed because addresses are save separately for now * @param array $extractedCustomerData * @return array */ @@ -188,7 +190,6 @@ public function execute() try { // optional fields might be set in request for future processing by observers in other modules $customerData = $this->_extractCustomerData(); - $addressesData = $this->_extractCustomerAddressData($customerData); if ($customerId) { $currentCustomer = $this->_customerRepository->getById($customerId); @@ -206,28 +207,12 @@ public function execute() $customerData, \Magento\Customer\Api\Data\CustomerInterface::class ); - $addresses = []; - foreach ($addressesData as $addressData) { - $region = isset($addressData['region']) ? $addressData['region'] : null; - $regionId = isset($addressData['region_id']) ? $addressData['region_id'] : null; - $addressData['region'] = [ - 'region' => $region, - 'region_id' => $regionId, - ]; - $addressDataObject = $this->addressDataFactory->create(); - $this->dataObjectHelper->populateWithArray( - $addressDataObject, - $addressData, - \Magento\Customer\Api\Data\AddressInterface::class - ); - $addresses[] = $addressDataObject; - } $this->_eventManager->dispatch( 'adminhtml_customer_prepare_save', ['customer' => $customer, 'request' => $this->getRequest()] ); - $customer->setAddresses($addresses); + if (isset($customerData['sendemail_store_id'])) { $customer->setStoreId($customerData['sendemail_store_id']); } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php index be09eb7daff76..67adf98d6c718 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php @@ -72,40 +72,6 @@ protected function _validateCustomer($response) return $customer; } - /** - * Customer address validation. - * - * @param \Magento\Framework\DataObject $response - * @return void - */ - protected function _validateCustomerAddress($response) - { - $addresses = $this->getRequest()->getPost('address'); - if (!is_array($addresses)) { - return; - } - foreach (array_keys($addresses) as $index) { - if ($index == '_template_') { - continue; - } - - $addressForm = $this->_formFactory->create('customer_address', 'adminhtml_customer_address'); - - $requestScope = sprintf('address/%s', $index); - $formData = $addressForm->extractData($this->getRequest(), $requestScope); - - $errors = $addressForm->validateData($formData); - if ($errors !== true) { - $messages = $response->hasMessages() ? $response->getMessages() : []; - foreach ($errors as $error) { - $messages[] = $error; - } - $response->setMessages($messages); - $response->setError(1); - } - } - } - /** * AJAX customer validation action * @@ -116,10 +82,7 @@ public function execute() $response = new \Magento\Framework\DataObject(); $response->setError(0); - $customer = $this->_validateCustomer($response); - if ($customer) { - $this->_validateCustomerAddress($response); - } + $this->_validateCustomer($response); $resultJson = $this->resultJsonFactory->create(); if ($response->getError()) { $response->setError(true); diff --git a/app/code/Magento/Customer/Model/Address/DataProvider.php b/app/code/Magento/Customer/Model/Address/DataProvider.php new file mode 100644 index 0000000000000..34f4b8b4eca89 --- /dev/null +++ b/app/code/Magento/Customer/Model/Address/DataProvider.php @@ -0,0 +1,574 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Model\Address; + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\ResourceModel\Address\CollectionFactory; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Type; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Ui\DataProvider\EavValidationRules; +use Magento\Ui\Component\Form\Field; +use Magento\Eav\Api\Data\AttributeInterface; +use Magento\Customer\Model\ResourceModel\Address\Attribute\Source\CountryWithWebsites; +use Magento\Customer\Model\Attribute; +use Magento\Framework\App\ObjectManager; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Customer\Model\Address; +use Magento\Customer\Model\FileProcessor; +use Magento\Customer\Model\FileProcessorFactory; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * Dataprovider for customer address grid. + */ +class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider +{ + /** + * Maximum file size allowed for file_uploader UI component + */ + const MAX_FILE_SIZE = 2097152; + + /** + * @var \Magento\Customer\Model\ResourceModel\Address\Collection + */ + protected $collection; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var array + */ + private $loadedData; + + /** + * @var Config + */ + private $eavConfig; + + /** + * EAV attribute properties to fetch from meta storage + * @var array + */ + private $metaProperties = [ + 'dataType' => 'frontend_input', + 'visible' => 'is_visible', + 'required' => 'is_required', + 'label' => 'frontend_label', + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + ]; + + /** + * Form element mapping + * + * @var array + */ + private $formElement = [ + 'text' => 'input', + 'hidden' => 'input', + 'boolean' => 'checkbox', + ]; + + /** + * @var EavValidationRules + */ + private $eavValidationRules; + + /** + * @var CountryWithWebsites + */ + private $countryWithWebsiteSource; + + /** + * Allow to manage attributes, even they are hidden on storefront + * + * @var bool + */ + private $allowToShowHiddenAttributes; + + /* + * @var ContextInterface + */ + private $context; + + /** + * File types allowed for file_uploader UI component + * + * @var array + */ + private $fileUploaderTypes = [ + 'image', + 'file', + ]; + + /** + * @var \Magento\Customer\Model\Config\Share + */ + private $shareConfig; + + /** + * @var FileProcessorFactory + */ + private $fileProcessorFactory; + + /** + * @var array + */ + private $bannedInputTypes = ['media_image']; + + /** + * @var array + */ + private $attributesToEliminate = [ + 'region', + 'vat_is_valid', + 'vat_request_date', + 'vat_request_id', + 'vat_request_success' + ]; + + /** + * DataProvider constructor. + * @param string $name + * @param string $primaryFieldName + * @param string $requestFieldName + * @param CollectionFactory $addressCollectionFactory + * @param CustomerRepositoryInterface $customerRepository + * @param Config $eavConfig + * @param EavValidationRules $eavValidationRules + * @param ContextInterface $context + * @param FileProcessorFactory $fileProcessorFactory + * @param \Magento\Customer\Model\Config\Share $shareConfig + * @param array $meta + * @param array $data + * @param bool $allowToShowHiddenAttributes + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function __construct( + $name, + $primaryFieldName, + $requestFieldName, + CollectionFactory $addressCollectionFactory, + CustomerRepositoryInterface $customerRepository, + Config $eavConfig, + EavValidationRules $eavValidationRules, + ContextInterface $context, + FileProcessorFactory $fileProcessorFactory, + \Magento\Customer\Model\Config\Share $shareConfig, + array $meta = [], + array $data = [], + $allowToShowHiddenAttributes = true + ) { + parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); + $this->collection = $addressCollectionFactory->create(); + $this->collection->addAttributeToSelect('*'); + $this->customerRepository = $customerRepository; + $this->eavValidationRules = $eavValidationRules; + $this->allowToShowHiddenAttributes = $allowToShowHiddenAttributes; + $this->context = $context; + $this->fileProcessorFactory = $fileProcessorFactory; + $this->shareConfig = $shareConfig; + $this->meta['general']['children'] = $this->getAttributesMeta( + $eavConfig->getEntityType('customer_address') + ); + } + + /** + * Get Addresses data and process customer default billing & shipping addresses + * + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function getData() + { + if (null !== $this->loadedData) { + return $this->loadedData; + } + $items = $this->collection->getItems(); + /** @var Address $item */ + foreach ($items as $item) { + $addressId = $item->getEntityId(); + $item->load($addressId); + $this->loadedData[$addressId] = $item->getData(); + $customerId = $this->loadedData[$addressId]['parent_id']; + /** @var \Magento\Customer\Model\Customer $customer */ + $customer = $this->customerRepository->getById($customerId); + $defaultBilling = $customer->getDefaultBilling(); + $defaultShipping = $customer->getDefaultShipping(); + $this->prepareAddressData($addressId, $this->loadedData, $defaultBilling, $defaultShipping); + $this->overrideFileUploaderData($item, $this->loadedData[$addressId]); + } + + if (null === $this->loadedData) { + $this->loadedData[''] = $this->getDefaultData(); + } + + return $this->loadedData; + } + + /** + * Prepare address data + * + * @param int $addressId + * @param array $addresses + * @param string|null $defaultBilling + * @param string|null $defaultShipping + * @return void + */ + private function prepareAddressData($addressId, array &$addresses, $defaultBilling, $defaultShipping) + { + if (null !== $defaultBilling && $addressId == $defaultBilling) { + $addresses[$addressId]['default_billing'] = '1'; + } + if (null !== $defaultShipping && $addressId == $defaultShipping) { + $addresses[$addressId]['default_shipping'] = '1'; + } + if (null !== $addresses[$addressId]['street'] && !is_array($addresses[$addressId]['street'])) { + $addresses[$addressId]['street'] = explode("\n", $addresses[$addressId]['street']); + } + } + + /** + * Get default customer data for adding new address + * + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @return array + */ + private function getDefaultData() + { + $parentId = $this->context->getRequestParam('parent_id'); + $customer = $this->customerRepository->getById($parentId); + $data = [ + 'parent_id' => $parentId, + 'firstname' => $customer->getFirstname(), + 'lastname' => $customer->getLastname() + ]; + + return $data; + } + + /** + * Override file uploader UI component data + * + * Overrides data for attributes with frontend_input equal to 'image' or 'file'. + * + * @param Address $entity + * @param array $entityData + * @return void + */ + private function overrideFileUploaderData($entity, array &$entityData) + { + $attributes = $entity->getAttributes(); + foreach ($attributes as $attribute) { + /** @var Attribute $attribute */ + if (in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { + $entityData[$attribute->getAttributeCode()] = $this->getFileUploaderData( + $entity->getEntityType(), + $attribute, + $entityData + ); + } + } + } + + /** + * Get attributes meta + * + * @param Type $entityType + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ + protected function getAttributesMeta(Type $entityType): array + { + $meta = []; + $attributes = $entityType->getAttributeCollection(); + /* @var AbstractAttribute $attribute */ + foreach ($attributes as $attribute) { + $this->processFrontendInput($attribute, $meta); + + $code = $attribute->getAttributeCode(); + + if (in_array($attribute->getFrontendInput(), $this->bannedInputTypes)) { + continue; + } + if (in_array($attribute->getAttributeCode(), $this->attributesToEliminate)) { + continue; + } + + // use getDataUsingMethod, since some getters are defined and apply additional processing of returning value + foreach ($this->metaProperties as $metaName => $origName) { + $value = $attribute->getDataUsingMethod($origName); + $meta[$code]['arguments']['data']['config'][$metaName] = ($metaName === 'label') ? __($value) : $value; + if ('frontend_input' === $origName) { + $meta[$code]['arguments']['data']['config']['formElement'] = $this->formElement[$value] ?? $value; + } + } + + if ($attribute->usesSource()) { + if ($code == AddressInterface::COUNTRY_ID) { + $meta[$code]['arguments']['data']['config']['options'] = $this->getCountryWithWebsiteSource() + ->getAllOptions(); + } else { + $meta[$code]['arguments']['data']['config']['options'] = $attribute->getSource()->getAllOptions(); + } + } + + $rules = $this->eavValidationRules->build($attribute, $meta[$code]['arguments']['data']['config']); + if (!empty($rules)) { + $meta[$code]['arguments']['data']['config']['validation'] = $rules; + } + + $meta[$code]['arguments']['data']['config']['componentType'] = Field::NAME; + $meta[$code]['arguments']['data']['config']['visible'] = $this->canShowAttribute($attribute); + + $this->overrideFileUploaderMetadata($entityType, $attribute, $meta[$code]['arguments']['data']['config']); + } + + $this->processWebsiteMeta($meta); + return $meta; + } + + /** + * Process attributes by frontend input type + * + * @param AttributeInterface $attribute + * @param array $meta + * @return void + */ + private function processFrontendInput(AttributeInterface $attribute, array &$meta) + { + $code = $attribute->getAttributeCode(); + if ($attribute->getFrontendInput() === 'boolean') { + $meta[$code]['arguments']['data']['config']['prefer'] = 'toggle'; + $meta[$code]['arguments']['data']['config']['valueMap'] = [ + 'true' => '1', + 'false' => '0', + ]; + } + } + + /** + * Retrieve Country With Websites Source + * + * @return CountryWithWebsites + * @deprecated 100.2.0 + */ + private function getCountryWithWebsiteSource(): CountryWithWebsites + { + if (!$this->countryWithWebsiteSource) { + $this->countryWithWebsiteSource = ObjectManager::getInstance()->get(CountryWithWebsites::class); + } + + return $this->countryWithWebsiteSource; + } + + /** + * Detect can we show attribute on specific form or not + * + * @param Attribute $customerAttribute + * @return bool + */ + private function canShowAttribute(AbstractAttribute $customerAttribute): bool + { + $userDefined = (bool) $customerAttribute->getIsUserDefined(); + if (!$userDefined) { + return $customerAttribute->getIsVisible(); + } + + $canShowOnForm = $this->canShowAttributeInForm($customerAttribute); + + return ($this->allowToShowHiddenAttributes && $canShowOnForm) || + (!$this->allowToShowHiddenAttributes && $canShowOnForm && $customerAttribute->getIsVisible()); + } + + /** + * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... + * + * @param Attribute $customerAttribute + * @return bool + */ + private function canShowAttributeInForm(AbstractAttribute $customerAttribute): bool + { + $isRegistration = $this->context->getRequestParam($this->getRequestFieldName()) === null; + + if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { + return is_array($customerAttribute->getUsedInForms()) && + ( + (in_array('customer_account_create', $customerAttribute->getUsedInForms()) && $isRegistration) || + (in_array('customer_account_edit', $customerAttribute->getUsedInForms()) && !$isRegistration) + ); + } + return is_array($customerAttribute->getUsedInForms()) && + in_array('customer_address_edit', $customerAttribute->getUsedInForms()); + } + + /** + * Override file uploader UI component metadata + * + * Overrides metadata for attributes with frontend_input equal to 'image' or 'file'. + * + * @param Type $entityType + * @param AbstractAttribute $attribute + * @param array $config + * @return void + */ + private function overrideFileUploaderMetadata( + Type $entityType, + AbstractAttribute $attribute, + array &$config + ) { + if (in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { + $maxFileSize = self::MAX_FILE_SIZE; + + if (isset($config['validation']['max_file_size'])) { + $maxFileSize = (int)$config['validation']['max_file_size']; + } + + $allowedExtensions = []; + + if (isset($config['validation']['file_extensions'])) { + $allowedExtensions = explode(',', $config['validation']['file_extensions']); + array_walk($allowedExtensions, function (&$value) { + $value = strtolower(trim($value)); + }); + } + + $allowedExtensions = implode(' ', $allowedExtensions); + + $entityTypeCode = $entityType->getEntityTypeCode(); + $url = $this->getFileUploadUrl($entityTypeCode); + + $config = [ + 'formElement' => 'fileUploader', + 'componentType' => 'fileUploader', + 'maxFileSize' => $maxFileSize, + 'allowedExtensions' => $allowedExtensions, + 'uploaderConfig' => [ + 'url' => $url, + ], + 'label' => $this->getMetadataValue($config, 'label'), + 'sortOrder' => $this->getMetadataValue($config, 'sortOrder'), + 'required' => $this->getMetadataValue($config, 'required'), + 'visible' => $this->getMetadataValue($config, 'visible'), + 'validation' => $this->getMetadataValue($config, 'validation'), + ]; + } + } + + /** + * Add global scope parameter and filter options to website meta + * + * @param array $meta + * @return void + */ + private function processWebsiteMeta(&$meta) + { + if (isset($meta[CustomerInterface::WEBSITE_ID]) && $this->shareConfig->isGlobalScope()) { + $meta[CustomerInterface::WEBSITE_ID]['arguments']['data']['config']['isGlobalScope'] = 1; + } + + if (isset($meta[AddressInterface::COUNTRY_ID]) && !$this->shareConfig->isGlobalScope()) { + $meta[AddressInterface::COUNTRY_ID]['arguments']['data']['config']['filterBy'] = [ + 'target' => 'customer_form.customer_form_data_source:data.customer.website_id', + 'field' => 'website_ids' + ]; + } + } + + /** + * Retrieve metadata value + * + * @param array $config + * @param string $name + * @param mixed $default + * @return mixed + */ + private function getMetadataValue($config, $name, $default = null) + { + return $config[$name] ?? $default; + } + + /** + * Retrieve URL to file upload + * + * @param string $entityTypeCode + * @return string + */ + private function getFileUploadUrl($entityTypeCode): string + { + switch ($entityTypeCode) { + case CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER: + $url = 'customer/file/customer_upload'; + break; + + case AddressMetadataInterface::ENTITY_TYPE_ADDRESS: + $url = 'customer/file/address_upload'; + break; + + default: + $url = ''; + break; + } + return $url; + } + + /** + * Retrieve array of values required by file uploader UI component + * + * @param Type $entityType + * @param Attribute $attribute + * @param array $customerData + * @return array + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + private function getFileUploaderData( + Type $entityType, + Attribute $attribute, + array $customerData + ): array { + $attributeCode = $attribute->getAttributeCode(); + + $file = $customerData[$attributeCode] ?? ''; + + /** @var FileProcessor $fileProcessor */ + $fileProcessor = $this->fileProcessorFactory->create([ + 'entityTypeCode' => $entityType->getEntityTypeCode(), + ]); + + if (!empty($file) + && $fileProcessor->isExist($file) + ) { + $stat = $fileProcessor->getStat($file); + $viewUrl = $fileProcessor->getViewUrl($file, $attribute->getFrontendInput()); + + return [ + [ + 'file' => $file, + 'size' => null !== $stat ? $stat['size'] : 0, + 'url' => $viewUrl ?? '', + 'name' => basename($file), + 'type' => $fileProcessor->getMimeType($file), + ], + ]; + } + + return []; + } +} diff --git a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php new file mode 100644 index 0000000000000..d52c94fb034c6 --- /dev/null +++ b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php @@ -0,0 +1,585 @@ +<?php +declare(strict_types=1); +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Model\Customer; + +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\Address; +use Magento\Customer\Model\Attribute; +use Magento\Customer\Model\Customer; +use Magento\Customer\Model\FileProcessor; +use Magento\Customer\Model\FileProcessorFactory; +use Magento\Customer\Model\ResourceModel\Address\Attribute\Source\CountryWithWebsites; +use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory as CustomerCollectionFactory; +use Magento\Eav\Api\Data\AttributeInterface; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Eav\Model\Entity\Type; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Session\SessionManagerInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Ui\Component\Form\Field; +use Magento\Ui\DataProvider\EavValidationRules; + +/** + * Refactored version of Magento\Customer\Model\Customer\DataProvider with eliminated usage of addresses collection. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class DataProviderWithDefaultAddresses extends \Magento\Ui\DataProvider\AbstractDataProvider +{ + /** + * Maximum file size allowed for file_uploader UI component + */ + const MAX_FILE_SIZE = 2097152; + + /** + * @var array + */ + private $loadedData; + + /** + * @var CountryWithWebsites + */ + private $countryWithWebsiteSource; + + /** + * @var \Magento\Customer\Model\Config\Share + */ + private $shareConfig; + + /** + * EAV attribute properties to fetch from meta storage + * @var array + */ + private $metaProperties = [ + 'dataType' => 'frontend_input', + 'visible' => 'is_visible', + 'required' => 'is_required', + 'label' => 'frontend_label', + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + ]; + + /** + * Form element mapping + * + * @var array + */ + private $formElement = [ + 'text' => 'input', + 'hidden' => 'input', + 'boolean' => 'checkbox', + ]; + + /** + * @var EavValidationRules + */ + private $eavValidationRules; + + /** + * @var SessionManagerInterface + * @since 100.1.0 + */ + private $session; + + /** + * @var FileProcessorFactory + */ + private $fileProcessorFactory; + + /** + * File types allowed for file_uploader UI component + * + * @var array + */ + private $fileUploaderTypes = [ + 'image', + 'file', + ]; + + /** + * Customer fields that must be removed + * + * @var array + */ + private $forbiddenCustomerFields = [ + 'password_hash', + 'rp_token', + 'confirmation', + ]; + + /* + * @var ContextInterface + */ + private $context; + + /** + * Allow to manage attributes, even they are hidden on storefront + * + * @var bool + */ + private $allowToShowHiddenAttributes; + + /** + * @var \Magento\Directory\Model\CountryFactory + */ + private $countryFactory; + + /** + * DataProviderWithDefaultAddresses constructor. + * + * @param string $name + * @param string $primaryFieldName + * @param string $requestFieldName + * @param EavValidationRules $eavValidationRules + * @param CustomerCollectionFactory $customerCollectionFactory + * @param Config $eavConfig + * @param FileProcessorFactory $fileProcessorFactory + * @param ContextInterface $context + * @param \Magento\Directory\Model\CountryFactory $countryFactory + * @param SessionManagerInterface $session + * @param \Magento\Customer\Model\Config\Share $shareConfig + * @param CountryWithWebsites $countryWithWebsites + * @param bool $allowToShowHiddenAttributes + * @param array $meta + * @param array $data + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function __construct( + string $name, + string $primaryFieldName, + string $requestFieldName, + EavValidationRules $eavValidationRules, + CustomerCollectionFactory $customerCollectionFactory, + Config $eavConfig, + FileProcessorFactory $fileProcessorFactory, + ContextInterface $context, + \Magento\Directory\Model\CountryFactory $countryFactory, + \Magento\Framework\Session\SessionManagerInterface $session, + \Magento\Customer\Model\Config\Share $shareConfig, + CountryWithWebsites $countryWithWebsites, + $allowToShowHiddenAttributes = true, + array $meta = [], + array $data = [] + ) { + parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); + $this->eavValidationRules = $eavValidationRules; + $this->collection = $customerCollectionFactory->create(); + $this->collection->addAttributeToSelect('*'); + $this->fileProcessorFactory = $fileProcessorFactory; + $this->context = $context ?: ObjectManager::getInstance()->get(ContextInterface::class); + $this->allowToShowHiddenAttributes = $allowToShowHiddenAttributes; + $this->session = $session; + $this->countryWithWebsiteSource = $countryWithWebsites; + $this->shareConfig = $shareConfig; + $this->countryFactory = $countryFactory; + $this->meta['customer']['children'] = $this->getAttributesMeta( + $eavConfig->getEntityType('customer') + ); +// $this->meta['address']['children'] = $this->getAttributesMeta( +// $eavConfig->getEntityType('customer_address') +// ); + } + + /** + * Get data + * + * @return array + */ + public function getData() + { + if (null !== $this->loadedData) { + return $this->loadedData; + } + $items = $this->collection->getItems(); + /** @var Customer $customer */ + foreach ($items as $customer) { + $result['customer'] = $customer->getData(); + + $this->overrideFileUploaderData($customer, $result['customer']); + + $result['customer'] = array_diff_key( + $result['customer'], + array_flip($this->forbiddenCustomerFields) + ); + unset($result['address']); + + $result['default_billing_address'] = $this->prepareDefaultAddress( + $customer->getDefaultBillingAddress() + ); + $result['default_shipping_address'] = $this->prepareDefaultAddress( + $customer->getDefaultShippingAddress() + ); + $result['customer_id'] = $customer->getId(); + + $this->loadedData[$customer->getId()] = $result; + } + + $data = $this->session->getCustomerFormData(); + if (!empty($data)) { + $customerId = $data['customer']['entity_id'] ?? null; + $this->loadedData[$customerId] = $data; + $this->session->unsCustomerFormData(); + } + + return $this->loadedData; + } + + /** + * Prepare default address data. + * + * @param Address|false $address + * @return array + */ + private function prepareDefaultAddress($address): array + { + $addressData = []; + + if (!empty($address)) { + $addressData = $address->getData(); + if (isset($addressData['street']) && !\is_array($address['street'])) { + $addressData['street'] = explode("\n", $addressData['street']); + } + $addressData['country'] = $this->countryFactory->create() + ->loadByCode($addressData['country_id'])->getName(); + } + + return $addressData; + } + + /** + * Override file uploader UI component data + * + * Overrides data for attributes with frontend_input equal to 'image' or 'file'. + * + * @param Customer|Address $entity + * @param array $entityData + * @return void + */ + private function overrideFileUploaderData($entity, array &$entityData) + { + $attributes = $entity->getAttributes(); + foreach ($attributes as $attribute) { + /** @var Attribute $attribute */ + if (\in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { + $entityData[$attribute->getAttributeCode()] = $this->getFileUploaderData( + $entity->getEntityType(), + $attribute, + $entityData + ); + } + } + } + + /** + * Retrieve array of values required by file uploader UI component + * + * @param Type $entityType + * @param Attribute $attribute + * @param array $customerData + * @return array + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + private function getFileUploaderData( + Type $entityType, + Attribute $attribute, + array $customerData + ): array { + $attributeCode = $attribute->getAttributeCode(); + + $file = $customerData[$attributeCode] ?? ''; + + /** @var FileProcessor $fileProcessor */ + $fileProcessor = $this->getFileProcessorFactory()->create([ + 'entityTypeCode' => $entityType->getEntityTypeCode(), + ]); + + if (!empty($file) + && $fileProcessor->isExist($file) + ) { + $stat = $fileProcessor->getStat($file); + $viewUrl = $fileProcessor->getViewUrl($file, $attribute->getFrontendInput()); + + return [ + [ + 'file' => $file, + 'size' => null !== $stat ? $stat['size'] : 0, + 'url' => $viewUrl ?? '', + 'name' => basename($file), + 'type' => $fileProcessor->getMimeType($file), + ], + ]; + } + + return []; + } + + /** + * Get attributes meta + * + * @param Type $entityType + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ + protected function getAttributesMeta(Type $entityType): array + { + $meta = []; + $attributes = $entityType->getAttributeCollection(); + /* @var AbstractAttribute $attribute */ + foreach ($attributes as $attribute) { + $this->processFrontendInput($attribute, $meta); + + $code = $attribute->getAttributeCode(); + + // use getDataUsingMethod, since some getters are defined and apply additional processing of returning value + foreach ($this->metaProperties as $metaName => $origName) { + $value = $attribute->getDataUsingMethod($origName); + $meta[$code]['arguments']['data']['config'][$metaName] = ($metaName === 'label') ? __($value) : $value; + if ('frontend_input' === $origName) { + $meta[$code]['arguments']['data']['config']['formElement'] = $this->formElement[$value] ?? $value; + } + } + + if ($attribute->usesSource()) { + if ($code == AddressInterface::COUNTRY_ID) { + $meta[$code]['arguments']['data']['config']['options'] = $this->countryWithWebsiteSource + ->getAllOptions(); + } else { + $meta[$code]['arguments']['data']['config']['options'] = $attribute->getSource()->getAllOptions(); + } + } + + $rules = $this->eavValidationRules->build($attribute, $meta[$code]['arguments']['data']['config']); + if (!empty($rules)) { + $meta[$code]['arguments']['data']['config']['validation'] = $rules; + } + + $meta[$code]['arguments']['data']['config']['componentType'] = Field::NAME; + $meta[$code]['arguments']['data']['config']['visible'] = $this->canShowAttribute($attribute); + + $this->overrideFileUploaderMetadata($entityType, $attribute, $meta[$code]['arguments']['data']['config']); + } + + $this->processWebsiteMeta($meta); + return $meta; + } + + /** + * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... + * + * @param AbstractAttribute $customerAttribute + * @return bool + */ + private function canShowAttributeInForm(AbstractAttribute $customerAttribute) + { + $isRegistration = $this->context->getRequestParam($this->getRequestFieldName()) === null; + + if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { + return \is_array($customerAttribute->getUsedInForms()) && + ( + (\in_array('customer_account_create', $customerAttribute->getUsedInForms()) && $isRegistration) || + (\in_array('customer_account_edit', $customerAttribute->getUsedInForms()) && !$isRegistration) + ); + } + return \is_array($customerAttribute->getUsedInForms()) && + \in_array('customer_address_edit', $customerAttribute->getUsedInForms()); + } + + /** + * Detect can we show attribute on specific form or not + * + * @param AbstractAttribute $customerAttribute + * @return bool + */ + private function canShowAttribute(AbstractAttribute $customerAttribute) + { + $userDefined = (bool) $customerAttribute->getIsUserDefined(); + if (!$userDefined) { + return $customerAttribute->getIsVisible(); + } + + $canShowOnForm = $this->canShowAttributeInForm($customerAttribute); + + return ($this->allowToShowHiddenAttributes && $canShowOnForm) || + (!$this->allowToShowHiddenAttributes && $canShowOnForm && $customerAttribute->getIsVisible()); + } + + /** + * Add global scope parameter and filter options to website meta + * + * @param array $meta + * @return void + */ + private function processWebsiteMeta(&$meta) + { + if (isset($meta[CustomerInterface::WEBSITE_ID]) && $this->shareConfig->isGlobalScope()) { + $meta[CustomerInterface::WEBSITE_ID]['arguments']['data']['config']['isGlobalScope'] = 1; + } + + if (isset($meta[AddressInterface::COUNTRY_ID]) && !$this->shareConfig->isGlobalScope()) { + $meta[AddressInterface::COUNTRY_ID]['arguments']['data']['config']['filterBy'] = [ + 'target' => '${ $.provider }:data.customer.website_id', + 'field' => 'website_ids' + ]; + } + } + + /** + * Override file uploader UI component metadata + * + * Overrides metadata for attributes with frontend_input equal to 'image' or 'file'. + * + * @param Type $entityType + * @param AbstractAttribute $attribute + * @param array $config + * @return void + */ + private function overrideFileUploaderMetadata( + Type $entityType, + AbstractAttribute $attribute, + array &$config + ) { + if (\in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { + $maxFileSize = self::MAX_FILE_SIZE; + + if (isset($config['validation']['max_file_size'])) { + $maxFileSize = (int)$config['validation']['max_file_size']; + } + + $allowedExtensions = []; + + if (isset($config['validation']['file_extensions'])) { + $allowedExtensions = explode(',', $config['validation']['file_extensions']); + array_walk($allowedExtensions, function (&$value) { + $value = strtolower(trim($value)); + }); + } + + $allowedExtensions = implode(' ', $allowedExtensions); + + $entityTypeCode = $entityType->getEntityTypeCode(); + $url = $this->getFileUploadUrl($entityTypeCode); + + $config = [ + 'formElement' => 'fileUploader', + 'componentType' => 'fileUploader', + 'maxFileSize' => $maxFileSize, + 'allowedExtensions' => $allowedExtensions, + 'uploaderConfig' => [ + 'url' => $url, + ], + 'label' => $this->getMetadataValue($config, 'label'), + 'sortOrder' => $this->getMetadataValue($config, 'sortOrder'), + 'required' => $this->getMetadataValue($config, 'required'), + 'visible' => $this->getMetadataValue($config, 'visible'), + 'validation' => $this->getMetadataValue($config, 'validation'), + ]; + } + } + + /** + * Retrieve metadata value + * + * @param array $config + * @param string $name + * @param mixed $default + * @return mixed + */ + private function getMetadataValue($config, $name, $default = null) + { + return $config[$name] ?? $default; + } + + /** + * Retrieve URL to file upload + * + * @param string $entityTypeCode + * @return string + */ + private function getFileUploadUrl($entityTypeCode): string + { + switch ($entityTypeCode) { + case CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER: + $url = 'customer/file/customer_upload'; + break; + + case AddressMetadataInterface::ENTITY_TYPE_ADDRESS: + $url = 'customer/file/address_upload'; + break; + + default: + $url = ''; + break; + } + return $url; + } + + /** + * Process attributes by frontend input type + * + * @param AttributeInterface $attribute + * @param array $meta + * @return void + */ + private function processFrontendInput(AttributeInterface $attribute, array &$meta) + { + $code = $attribute->getAttributeCode(); + if ($attribute->getFrontendInput() === 'boolean') { + $meta[$code]['arguments']['data']['config']['prefer'] = 'toggle'; + $meta[$code]['arguments']['data']['config']['valueMap'] = [ + 'true' => '1', + 'false' => '0', + ]; + } + } + + /** + * Prepare address data + * + * @param int $addressId + * @param array $addresses + * @param array $customer + * @return void + */ + protected function prepareAddressData($addressId, array &$addresses, array $customer) + { + if (isset($customer['default_billing']) + && $addressId == $customer['default_billing'] + ) { + $addresses[$addressId]['default_billing'] = $customer['default_billing']; + } + if (isset($customer['default_shipping']) + && $addressId == $customer['default_shipping'] + ) { + $addresses[$addressId]['default_shipping'] = $customer['default_shipping']; + } + if (isset($addresses[$addressId]['street']) && !\is_array($addresses[$addressId]['street'])) { + $addresses[$addressId]['street'] = explode("\n", $addresses[$addressId]['street']); + } + } + + /** + * Get FileProcessorFactory instance + * + * @return FileProcessorFactory + * @deprecated 100.1.3 + */ + private function getFileProcessorFactory(): FileProcessorFactory + { + if ($this->fileProcessorFactory === null) { + $this->fileProcessorFactory = ObjectManager::getInstance() + ->get(\Magento\Customer\Model\FileProcessorFactory::class); + } + return $this->fileProcessorFactory; + } +} diff --git a/app/code/Magento/Customer/Model/Metadata/Form/File.php b/app/code/Magento/Customer/Model/Metadata/Form/File.php index e6e9c2b50c068..aca5b277186ca 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/File.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/File.php @@ -109,7 +109,7 @@ public function extractValue(\Magento\Framework\App\RequestInterface $request) $extend = $this->_getRequestValue($request); $attrCode = $this->getAttribute()->getAttributeCode(); - if ($this->_requestScope) { + if ($this->_requestScope || !isset($_FILES[$attrCode])) { $value = []; if (strpos($this->_requestScope, '/') !== false) { $scopes = explode('/', $this->_requestScope); diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php new file mode 100644 index 0000000000000..83129fee9b59b --- /dev/null +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php @@ -0,0 +1,171 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Model\ResourceModel\Address\Grid; + +use Magento\Framework\Api\Search\SearchResultInterface; +use Magento\Framework\Api\Search\AggregationInterface; +use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection; +use Magento\Framework\View\Element\UiComponent\Context; + +/** + * Class getting collection of addresses assigned to customer + */ +class Collection extends AbstractCollection implements SearchResultInterface +{ + /** + * @var string + */ + protected $_idFieldName = 'entity_id'; + + /** + * @var Context + */ + private $context; + + /** + * @var AggregationInterface + */ + protected $aggregations; + + /** + * @param Context $context + * @param \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory + * @param \Psr\Log\LoggerInterface $logger + * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy + * @param \Magento\Framework\Event\ManagerInterface $eventManager + * @param string $mainTable + * @param string $eventPrefix + * @param string $eventObject + * @param string $resourceModel + * @param string $model + * @param \Magento\Framework\DB\Adapter\AdapterInterface|string|null $connection + * @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function __construct( + Context $context, + \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory, + \Psr\Log\LoggerInterface $logger, + \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, + \Magento\Framework\Event\ManagerInterface $eventManager, + $mainTable, + $eventPrefix, + $eventObject, + $resourceModel, + $model = \Magento\Framework\View\Element\UiComponent\DataProvider\Document::class, + $connection = null, + \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null + ) { + $this->context = $context; + $this->_eventPrefix = $eventPrefix; + $this->_eventObject = $eventObject; + $this->_init($model, $resourceModel); + $this->setMainTable($mainTable); + parent::__construct( + $entityFactory, + $logger, + $fetchStrategy, + $eventManager, + $connection, + $resource + ); + } + + /** + * Resource initialization + * + * @return $this + */ + protected function _initSelect() + { + parent::_initSelect(); + $parentId = $this->context->getRequestParam('parent_id'); + if ($parentId !== null) { + $this->getSelect()->where('parent_id=?', $parentId); + } + + return $this; + } + + /** + * {@inheritdoc} + * + * @return AggregationInterface + */ + public function getAggregations() + { + return $this->aggregations; + } + + /** + * {@inheritdoc} + * + * @param AggregationInterface $aggregations + * @return $this + */ + public function setAggregations($aggregations) + { + $this->aggregations = $aggregations; + return $this; + } + + /** + * Get search criteria. + * + * @return \Magento\Framework\Api\SearchCriteriaInterface|null + */ + public function getSearchCriteria() + { + return null; + } + + /** + * Set search criteria. + * + * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria + * @return $this + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function setSearchCriteria(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria = null) + { + return $this; + } + + /** + * Get total count. + * + * @return int + */ + public function getTotalCount() + { + return $this->getSize(); + } + + /** + * Set total count. + * + * @param int $totalCount + * @return $this + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function setTotalCount($totalCount) + { + return $this; + } + + /** + * Set items list. + * + * @param \Magento\Framework\Api\ExtensibleDataInterface[] $items + * @return $this + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function setItems(array $items = null) + { + return $this; + } +} diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php index d473a4dc01891..cbfebe87812bc 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php @@ -38,16 +38,26 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) /** * @var $object \Magento\Customer\Model\Address */ - if (!$object->getIsCustomerSaveTransaction() && $this->isAddressDefault($object)) { + if (!$object->getIsCustomerSaveTransaction() && $object->getId()) { $customer = $this->customerFactory->create()->load($object->getCustomerId()); $changedAddresses = []; if ($object->getIsDefaultBilling()) { $changedAddresses['default_billing'] = $object->getId(); + } elseif ($customer->getDefaultBillingAddress() + && (int)$customer->getDefaultBillingAddress()->getId() === (int)$object->getId() + && !$object->getIsDefaultBilling() + ) { + $changedAddresses['default_billing'] = null; } if ($object->getIsDefaultShipping()) { $changedAddresses['default_shipping'] = $object->getId(); + } elseif ($customer->getDefaultShippingAddress() + && (int)$customer->getDefaultShippingAddress()->getId() === (int)$object->getId() + && !$object->getIsDefaultShipping() + ) { + $changedAddresses['default_shipping'] = null; } if ($changedAddresses) { @@ -63,6 +73,9 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) /** * Checks if address has chosen as default and has had an id * + * @deprecated Is not used anymore due to changes in logic of save of address. + * If address was default and becomes not default than default address id for customer must be + * set to null * @param \Magento\Framework\Model\AbstractModel $object * @return bool */ diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php index 43ae2db0c2163..5d88cd92c1730 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php @@ -170,22 +170,18 @@ public function save(CustomerInterface $customer, $passwordHash = null) /** @var NewOperation|null $delegatedNewOperation */ $delegatedNewOperation = !$customer->getId() ? $this->delegatedStorage->consumeNewOperation() : null; $prevCustomerData = null; - $prevCustomerDataArr = null; if ($customer->getId()) { $prevCustomerData = $this->getById($customer->getId()); - $prevCustomerDataArr = $prevCustomerData->__toArray(); } - /** @var $customer \Magento\Customer\Model\Data\Customer */ - $customerArr = $customer->__toArray(); + $customer = $this->imageProcessor->save( $customer, CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, $prevCustomerData ); - $origAddresses = $customer->getAddresses(); - $customer->setAddresses([]); + + /** @var array $customerData */ $customerData = $this->extensibleDataObjectConverter->toNestedArray($customer, [], CustomerInterface::class); - $customer->setAddresses($origAddresses); /** @var Customer $customerModel */ $customerModel = $this->customerFactory->create(['data' => $customerData]); //Model's actual ID field maybe different than "id" so "id" field from $customerData may be ignored. @@ -200,51 +196,10 @@ public function save(CustomerInterface $customer, $passwordHash = null) $customerModel->setRpToken(null); $customerModel->setRpTokenCreatedAt(null); } - if (!array_key_exists('default_billing', $customerArr) - && null !== $prevCustomerDataArr - && array_key_exists('default_billing', $prevCustomerDataArr) - ) { - $customerModel->setDefaultBilling($prevCustomerDataArr['default_billing']); - } - if (!array_key_exists('default_shipping', $customerArr) - && null !== $prevCustomerDataArr - && array_key_exists('default_shipping', $prevCustomerDataArr) - ) { - $customerModel->setDefaultShipping($prevCustomerDataArr['default_shipping']); - } $customerModel->save(); $this->customerRegistry->push($customerModel); $customerId = $customerModel->getId(); - if (!$customer->getAddresses() - && $delegatedNewOperation - && $delegatedNewOperation->getCustomer()->getAddresses() - ) { - $customer->setAddresses($delegatedNewOperation->getCustomer()->getAddresses()); - } - if ($customer->getAddresses() !== null) { - if ($customer->getId()) { - $existingAddresses = $this->getById($customer->getId())->getAddresses(); - $getIdFunc = function ($address) { - return $address->getId(); - }; - $existingAddressIds = array_map($getIdFunc, $existingAddresses); - } else { - $existingAddressIds = []; - } - $savedAddressIds = []; - foreach ($customer->getAddresses() as $address) { - $address->setCustomerId($customerId) - ->setRegion($address->getRegion()); - $this->addressRepository->save($address); - if ($address->getId()) { - $savedAddressIds[] = $address->getId(); - } - } - $addressIdsToDelete = array_diff($existingAddressIds, $savedAddressIds); - foreach ($addressIdsToDelete as $addressId) { - $this->addressRepository->deleteById($addressId); - } - } + $this->customerRegistry->remove($customerId); $savedCustomer = $this->get($customer->getEmail(), $customer->getWebsiteId()); $this->eventManager->dispatch( @@ -374,7 +329,6 @@ public function deleteById($customerId) * @param \Magento\Framework\Api\Search\FilterGroup $filterGroup * @param \Magento\Customer\Model\ResourceModel\Customer\Collection $collection * @return void - * @throws \Magento\Framework\Exception\InputException */ protected function addFilterGroupToCollection( \Magento\Framework\Api\Search\FilterGroup $filterGroup, diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php new file mode 100644 index 0000000000000..47088eece23ed --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php @@ -0,0 +1,205 @@ +<?php +declare(strict_types=1); +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Test\Unit\Controller\Address; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class SaveTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Customer\Controller\Adminhtml\Address\Save + */ + private $model; + + /** + * @var \Magento\Customer\Api\AddressRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $addressRepositoryMock; + + + /** + * @var \Magento\Customer\Model\Metadata\FormFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $formFactoryMock; + + /** + * @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerRepositoryMock; + + /** + * @var \Magento\Framework\Api\DataObjectHelper|\PHPUnit_Framework_MockObject_MockObject + */ + private $dataObjectHelperMock; + + /** + * @var \Magento\Customer\Api\Data\AddressInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $addressDataFactoryMock; + + /** + * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $loggerMock; + + /** + * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $requestMock; + + /** + * @var \Magento\Backend\Model\View\Result\RedirectFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultRedirectFactoryMock; + + /** + * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $messageManagerMock; + + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->addressRepositoryMock = $this->createMock(\Magento\Customer\Api\AddressRepositoryInterface::class); + $this->formFactoryMock = $this->createMock(\Magento\Customer\Model\Metadata\FormFactory::class); + $this->customerRepositoryMock = $this->createMock(\Magento\Customer\Api\CustomerRepositoryInterface::class); + $this->dataObjectHelperMock = $this->createMock(\Magento\Framework\Api\DataObjectHelper ::class); + $this->addressDataFactoryMock = $this->createMock(\Magento\Customer\Api\Data\AddressInterfaceFactory::class); + $this->loggerMock = $this->createMock(\Psr\Log\LoggerInterface::class); + $this->requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); + $this->resultRedirectFactoryMock = $this->createMock(\Magento\Backend\Model\View\Result\RedirectFactory::class); + $this->messageManagerMock = $this->createMock(\Magento\Framework\Message\ManagerInterface::class); + + $objectManager = new ObjectManagerHelper($this); + + $this->model = $objectManager->getObject( + \Magento\Customer\Controller\Adminhtml\Address\Save::class, + [ + 'addressRepository' => $this->addressRepositoryMock, + 'formFactory' => $this->formFactoryMock, + 'customerRepository' => $this->customerRepositoryMock, + 'dataObjectHelper' => $this->dataObjectHelperMock, + 'addressDataFactory' => $this->addressDataFactoryMock, + 'loggerMock' => $this->loggerMock, + 'request' => $this->requestMock, + 'resultRedirectFactory' => $this->resultRedirectFactoryMock, + 'messageManager' => $this->messageManagerMock, + ] + ); + } + + /** + * Test method \Magento\Customer\Controller\Adminhtml\Address\Save::execute + * + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testExecute() + { + $addressId = 11; + $customerId = 22; + + $addressExtractedData = [ + 'entity_id' => $addressId, + 'code' => 'value', + 'coolness' => false, + 'region' => 'region', + 'region_id' => 'region_id', + ]; + + $addressCompactedData = [ + 'entity_id' => $addressId, + 'default_billing' => 'true', + 'default_shipping' => 'true', + 'code' => 'value', + 'coolness' => false, + 'region' => 'region', + 'region_id' => 'region_id', + ]; + + $mergedAddressData = [ + 'entity_id' => $addressId, + 'default_billing' => true, + 'default_shipping' => true, + 'code' => 'value', + 'region' => [ + 'region' => 'region', + 'region_id' => 'region_id', + ], + 'region_id' => 'region_id', + 'id' => $addressId, + ]; + + $this->requestMock->method('getParam') + ->withConsecutive(['parent_id'], ['entity_id']) + ->willReturnOnConsecutiveCalls(22, 1); + + $customerMock = $this->getMockBuilder( + \Magento\Customer\Api\Data\CustomerInterface::class + )->disableOriginalConstructor()->getMock(); + + $this->customerRepositoryMock->expects($this->atLeastOnce()) + ->method('getById') + ->with($customerId) + ->willReturn($customerMock); + + $customerAddressFormMock = $this->createMock(\Magento\Customer\Model\Metadata\Form::class); + + $customerAddressFormMock->expects($this->atLeastOnce()) + ->method('extractData') + ->with($this->requestMock) + ->willReturn($addressExtractedData); + $customerAddressFormMock->expects($this->once()) + ->method('compactData') + ->with($addressExtractedData) + ->willReturn($addressCompactedData); + + $this->formFactoryMock->expects($this->exactly(1)) + ->method('create') + ->willReturn($customerAddressFormMock); + + $addressMock = $this->getMockBuilder(\Magento\Customer\Api\Data\AddressInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->addressDataFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($addressMock); + + $this->dataObjectHelperMock->expects($this->atLeastOnce()) + ->method('populateWithArray') + ->willReturn( + [ + $addressMock, + $mergedAddressData, \Magento\Customer\Api\Data\AddressInterface::class, + $this->dataObjectHelperMock, + ] + ); + + $this->messageManagerMock->expects($this->once()) + ->method('addSuccessMessage') + ->with(__('Customer address has been updated.')) + ->willReturnSelf(); + + $resultRedirect = $this->createMock(\Magento\Framework\Controller\Result\Redirect::class); + $resultRedirect->expects($this->atLeastOnce()) + ->method('setPath') + ->with('customer/index/edit', ['id' => $customerId, '_current' => true]) + ->willReturnSelf(); + $this->resultRedirectFactoryMock->method('create') + ->willReturn($resultRedirect); + + $this->assertEquals($resultRedirect, $this->model->execute()); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/ValidateTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/ValidateTest.php new file mode 100644 index 0000000000000..a724bdd24959b --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/ValidateTest.php @@ -0,0 +1,118 @@ +<?php +declare(strict_types=1); +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Test\Unit\Controller\Address; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ValidateTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Customer\Controller\Adminhtml\Address\Validate + */ + private $model; + + /** + * @var \Magento\Customer\Model\Metadata\FormFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $formFactoryMock; + + /** + * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $requestMock; + + /** + * @var \Magento\Backend\Model\View\Result\RedirectFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultRedirectFactoryMock; + + /** + * @var \Magento\Framework\Controller\Result\JsonFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultJsonFactoryMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->formFactoryMock = $this->createMock(\Magento\Customer\Model\Metadata\FormFactory::class); + $this->requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); + $this->resultJsonFactoryMock = $this->createMock(\Magento\Framework\Controller\Result\JsonFactory::class); + $this->resultRedirectFactoryMock = $this->createMock(\Magento\Backend\Model\View\Result\RedirectFactory::class); + + $objectManager = new ObjectManagerHelper($this); + + $this->model = $objectManager->getObject( + \Magento\Customer\Controller\Adminhtml\Address\Validate::class, + [ + 'formFactory' => $this->formFactoryMock, + 'request' => $this->requestMock, + 'resultRedirectFactory' => $this->resultRedirectFactoryMock, + 'resultJsonFactory' => $this->resultJsonFactoryMock, + ] + ); + } + + /** + * Test method \Magento\Customer\Controller\Adminhtml\Address\Save::execute + * + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testExecute() + { + $addressId = 11; + $errors = ['Error Message 1', 'Error Message 2']; + + $addressExtractedData = [ + 'entity_id' => $addressId, + 'default_billing' => true, + 'default_shipping' => true, + 'code' => 'value', + 'region' => [ + 'region' => 'region', + 'region_id' => 'region_id', + ], + 'region_id' => 'region_id', + 'id' => $addressId, + ]; + + $customerAddressFormMock = $this->createMock(\Magento\Customer\Model\Metadata\Form::class); + + $customerAddressFormMock->expects($this->atLeastOnce()) + ->method('extractData') + ->with($this->requestMock) + ->willReturn($addressExtractedData); + $customerAddressFormMock->expects($this->once()) + ->method('validateData') + ->with($addressExtractedData) + ->willReturn($errors); + + $this->formFactoryMock->expects($this->exactly(1)) + ->method('create') + ->willReturn($customerAddressFormMock); + + $resultJson = $this->createMock(\Magento\Framework\Controller\Result\Json::class); + $this->resultJsonFactoryMock->method('create') + ->willReturn($resultJson); + + $validateResponseMock = $this->createPartialMock( + \Magento\Framework\DataObject::class, + ['getError', 'setMessages'] + ); + $validateResponseMock->method('setMessages')->willReturnSelf(); + $validateResponseMock->method('getError')->willReturn(1); + + $resultJson->method('setData')->willReturnSelf(); + + $this->assertEquals($resultJson, $this->model->execute()); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php index 5372bb11a89b5..c52d5b2fb370f 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php @@ -5,7 +5,6 @@ */ namespace Magento\Customer\Test\Unit\Controller\Adminhtml\Index; -use Magento\Customer\Api\AddressMetadataInterface; use Magento\Customer\Api\CustomerMetadataInterface; use Magento\Customer\Api\Data\AttributeMetadataInterface; use Magento\Customer\Api\Data\CustomerInterface; @@ -281,7 +280,6 @@ protected function setUp() public function testExecuteWithExistentCustomer() { $customerId = 22; - $addressId = 11; $subscription = 'true'; $postValue = [ 'customer' => [ @@ -290,18 +288,6 @@ public function testExecuteWithExistentCustomer() 'coolness' => false, 'disable_auto_group_change' => 'false', ], - 'address' => [ - '_template_' => '_template_', - $addressId => [ - 'entity_id' => $addressId, - 'default_billing' => 'true', - 'default_shipping' => 'true', - 'code' => 'value', - 'coolness' => false, - 'region' => 'region', - 'region_id' => 'region_id', - ], - ], 'subscription' => $subscription, ]; $extractedData = [ @@ -318,22 +304,6 @@ public function testExecuteWithExistentCustomer() CustomerInterface::DEFAULT_BILLING => 2, CustomerInterface::DEFAULT_SHIPPING => 2 ]; - $addressExtractedData = [ - 'entity_id' => $addressId, - 'code' => 'value', - 'coolness' => false, - 'region' => 'region', - 'region_id' => 'region_id', - ]; - $addressCompactedData = [ - 'entity_id' => $addressId, - 'default_billing' => 'true', - 'default_shipping' => 'true', - 'code' => 'value', - 'coolness' => false, - 'region' => 'region', - 'region_id' => 'region_id', - ]; $savedData = [ 'entity_id' => $customerId, 'darkness' => true, @@ -341,61 +311,40 @@ public function testExecuteWithExistentCustomer() CustomerInterface::DEFAULT_BILLING => false, CustomerInterface::DEFAULT_SHIPPING => false, ]; - $savedAddressData = [ - 'entity_id' => $addressId, - 'default_billing' => true, - 'default_shipping' => true, - ]; $mergedData = [ 'entity_id' => $customerId, 'darkness' => true, 'name' => 'Name', 'code' => 'value', 'disable_auto_group_change' => 0, - CustomerInterface::DEFAULT_BILLING => $addressId, - CustomerInterface::DEFAULT_SHIPPING => $addressId, 'confirmation' => false, 'sendemail_store_id' => '1', 'id' => $customerId, ]; - $mergedAddressData = [ - 'entity_id' => $addressId, - 'default_billing' => true, - 'default_shipping' => true, - 'code' => 'value', - 'region' => [ - 'region' => 'region', - 'region_id' => 'region_id', - ], - 'region_id' => 'region_id', - 'id' => $addressId, - ]; /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( \Magento\Customer\Api\Data\AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); - $attributeMock->expects($this->exactly(2)) + $attributeMock->expects($this->atLeastOnce()) ->method('getAttributeCode') ->willReturn('coolness'); - $attributeMock->expects($this->exactly(2)) + $attributeMock->expects($this->atLeastOnce()) ->method('getFrontendInput') ->willReturn('int'); $attributes = [$attributeMock]; - $this->requestMock->expects($this->any()) + $this->requestMock->expects($this->atLeastOnce()) ->method('getPostValue') ->willReturnMap([ [null, null, $postValue], [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], - ['address/' . $addressId, null, $postValue['address'][$addressId]], ]); - $this->requestMock->expects($this->exactly(3)) + $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( [ ['customer', null, $postValue['customer']], - ['address', null, $postValue['address']], ['subscription', null, $subscription], ] ); @@ -404,16 +353,15 @@ public function testExecuteWithExistentCustomer() $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() ->getMock(); - $objectMock->expects($this->exactly(2)) + $objectMock->expects($this->atLeastOnce()) ->method('getData') ->willReturnMap( [ ['customer', null, $postValue['customer']], - ['address/' . $addressId, null, $postValue['address'][$addressId]], ] ); - $this->objectFactoryMock->expects($this->exactly(2)) + $this->objectFactoryMock->expects($this->exactly(1)) ->method('create') ->with(['data' => $postValue]) ->willReturn($objectMock); @@ -432,23 +380,7 @@ public function testExecuteWithExistentCustomer() $customerFormMock->expects($this->once()) ->method('getAttributes') ->willReturn($attributes); - - $customerAddressFormMock = $this->getMockBuilder( - \Magento\Customer\Model\Metadata\Form::class - )->disableOriginalConstructor()->getMock(); - $customerAddressFormMock->expects($this->once()) - ->method('extractData') - ->with($this->requestMock, 'address/' . $addressId) - ->willReturn($addressExtractedData); - $customerAddressFormMock->expects($this->once()) - ->method('compactData') - ->with($addressExtractedData) - ->willReturn($addressCompactedData); - $customerAddressFormMock->expects($this->once()) - ->method('getAttributes') - ->willReturn($attributes); - - $this->formFactoryMock->expects($this->exactly(2)) + $this->formFactoryMock->expects($this->exactly(1)) ->method('create') ->willReturnMap( [ @@ -461,15 +393,6 @@ public function testExecuteWithExistentCustomer() [], $customerFormMock ], - [ - AddressMetadataInterface::ENTITY_TYPE_ADDRESS, - 'adminhtml_customer_address', - $savedAddressData, - false, - Form::DONT_IGNORE_INVISIBLE, - [], - $customerAddressFormMock - ], ] ); @@ -492,25 +415,7 @@ public function testExecuteWithExistentCustomer() ->with($customerMock) ->willReturn($savedData); - $addressMock = $this->getMockBuilder(\Magento\Customer\Api\Data\AddressInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->customerAddressRepositoryMock->expects($this->once()) - ->method('getById') - ->with($addressId) - ->willReturn($addressMock); - - $this->customerAddressMapperMock->expects($this->once()) - ->method('toFlatArray') - ->with($addressMock) - ->willReturn($savedAddressData); - - $this->addressDataFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($addressMock); - - $this->dataHelperMock->expects($this->exactly(2)) + $this->dataHelperMock->expects($this->atLeastOnce()) ->method('populateWithArray') ->willReturnMap( [ @@ -519,19 +424,9 @@ public function testExecuteWithExistentCustomer() $mergedData, \Magento\Customer\Api\Data\CustomerInterface::class, $this->dataHelperMock ], - [ - $addressMock, - $mergedAddressData, \Magento\Customer\Api\Data\AddressInterface::class, - $this->dataHelperMock - ], ] ); - $customerMock->expects($this->once()) - ->method('setAddresses') - ->with([$addressMock]) - ->willReturnSelf(); - $this->customerRepositoryMock->expects($this->once()) ->method('save') ->with($customerMock) @@ -608,63 +503,33 @@ public function testExecuteWithExistentCustomer() public function testExecuteWithNewCustomer() { $customerId = 22; - $addressId = 11; + $subscription = '0'; $postValue = [ 'customer' => [ 'coolness' => false, 'disable_auto_group_change' => 'false', ], - 'address' => [ - '_template_' => '_template_', - $addressId => [ - 'entity_id' => $addressId, - 'code' => 'value', - 'coolness' => false, - 'region' => 'region', - 'region_id' => 'region_id', - ], - ], 'subscription' => $subscription, ]; $extractedData = [ 'coolness' => false, 'disable_auto_group_change' => 'false', ]; - $addressExtractedData = [ - 'entity_id' => $addressId, - 'code' => 'value', - 'coolness' => false, - 'region' => 'region', - 'region_id' => 'region_id', - ]; $mergedData = [ 'disable_auto_group_change' => 0, CustomerInterface::DEFAULT_BILLING => null, CustomerInterface::DEFAULT_SHIPPING => null, 'confirmation' => false, ]; - $mergedAddressData = [ - 'entity_id' => $addressId, - 'default_billing' => false, - 'default_shipping' => false, - 'code' => 'value', - 'region' => [ - 'region' => 'region', - 'region_id' => 'region_id', - ], - 'region_id' => 'region_id', - 'id' => $addressId, - ]; - /** @var AttributeMetadataInterface|\PHPUnit_Framework_MockObject_MockObject $customerFormMock */ $attributeMock = $this->getMockBuilder( \Magento\Customer\Api\Data\AttributeMetadataInterface::class )->disableOriginalConstructor()->getMock(); - $attributeMock->expects($this->exactly(2)) + $attributeMock->expects($this->atLeastOnce()) ->method('getAttributeCode') ->willReturn('coolness'); - $attributeMock->expects($this->exactly(2)) + $attributeMock->expects($this->atLeastOnce()) ->method('getFrontendInput') ->willReturn('int'); $attributes = [$attributeMock]; @@ -674,14 +539,12 @@ public function testExecuteWithNewCustomer() ->willReturnMap([ [null, null, $postValue], [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], - ['address/' . $addressId, null, $postValue['address'][$addressId]], ]); - $this->requestMock->expects($this->exactly(3)) + $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( [ ['customer', null, $postValue['customer']], - ['address', null, $postValue['address']], ['subscription', null, $subscription], ] ); @@ -690,16 +553,15 @@ public function testExecuteWithNewCustomer() $objectMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) ->disableOriginalConstructor() ->getMock(); - $objectMock->expects($this->exactly(2)) + $objectMock->expects($this->atLeastOnce()) ->method('getData') ->willReturnMap( [ ['customer', null, $postValue['customer']], - ['address/' . $addressId, null, $postValue['address'][$addressId]], ] ); - $this->objectFactoryMock->expects($this->exactly(2)) + $this->objectFactoryMock->expects($this->atLeastOnce()) ->method('create') ->with(['data' => $postValue]) ->willReturn($objectMock); @@ -719,22 +581,7 @@ public function testExecuteWithNewCustomer() ->method('getAttributes') ->willReturn($attributes); - $customerAddressFormMock = $this->getMockBuilder( - \Magento\Customer\Model\Metadata\Form::class - )->disableOriginalConstructor()->getMock(); - $customerAddressFormMock->expects($this->once()) - ->method('extractData') - ->with($this->requestMock, 'address/' . $addressId) - ->willReturn($addressExtractedData); - $customerAddressFormMock->expects($this->once()) - ->method('compactData') - ->with($addressExtractedData) - ->willReturn($addressExtractedData); - $customerAddressFormMock->expects($this->once()) - ->method('getAttributes') - ->willReturn($attributes); - - $this->formFactoryMock->expects($this->exactly(2)) + $this->formFactoryMock->expects($this->exactly(1)) ->method('create') ->willReturnMap( [ @@ -747,15 +594,6 @@ public function testExecuteWithNewCustomer() [], $customerFormMock ], - [ - AddressMetadataInterface::ENTITY_TYPE_ADDRESS, - 'adminhtml_customer_address', - [], - false, - Form::DONT_IGNORE_INVISIBLE, - [], - $customerAddressFormMock - ], ] ); @@ -768,25 +606,7 @@ public function testExecuteWithNewCustomer() ->method('create') ->willReturn($customerMock); - $addressMock = $this->getMockBuilder(\Magento\Customer\Api\Data\AddressInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->addressDataFactoryMock->expects($this->once()) - ->method('create') - ->willReturn($addressMock); - - $this->customerAddressRepositoryMock->expects($this->once()) - ->method('getById') - ->with($addressId) - ->willReturn($addressMock); - - $this->customerAddressMapperMock->expects($this->once()) - ->method('toFlatArray') - ->with($addressMock) - ->willReturn([]); - - $this->dataHelperMock->expects($this->exactly(2)) + $this->dataHelperMock->expects($this->atLeastOnce()) ->method('populateWithArray') ->willReturnMap( [ @@ -795,11 +615,6 @@ public function testExecuteWithNewCustomer() $mergedData, \Magento\Customer\Api\Data\CustomerInterface::class, $this->dataHelperMock ], - [ - $addressMock, - $mergedAddressData, \Magento\Customer\Api\Data\AddressInterface::class, - $this->dataHelperMock - ], ] ); @@ -904,12 +719,11 @@ public function testExecuteWithNewCustomerAndValidationException() [null, null, $postValue], [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], ]); - $this->requestMock->expects($this->exactly(2)) + $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( [ ['customer', null, $postValue['customer']], - ['address', null, null], ] ); @@ -1047,12 +861,11 @@ public function testExecuteWithNewCustomerAndLocalizedException() [null, null, $postValue], [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], ]); - $this->requestMock->expects($this->exactly(2)) + $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( [ ['customer', null, $postValue['customer']], - ['address', null, null], ] ); @@ -1190,12 +1003,11 @@ public function testExecuteWithNewCustomerAndException() [null, null, $postValue], [CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, null, $postValue['customer']], ]); - $this->requestMock->expects($this->exactly(2)) + $this->requestMock->expects($this->atLeastOnce()) ->method('getPost') ->willReturnMap( [ ['customer', null, $postValue['customer']], - ['address', null, null], ] ); diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ValidateTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ValidateTest.php index 7209ac9fd24b0..5adb902601630 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ValidateTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ValidateTest.php @@ -141,12 +141,6 @@ protected function setUp() public function testExecute() { - $this->request->expects($this->once()) - ->method('getPost') - ->willReturn([ - '_template_' => null, - 'address_index' => null - ]); $customerEntityId = 2; $this->request->expects($this->once()) ->method('getParam') @@ -162,11 +156,6 @@ public function testExecute() $this->form->expects($this->once())->method('setInvisibleIgnored'); $this->form->expects($this->atLeastOnce())->method('extractData')->willReturn([]); - $error = $this->createMock(\Magento\Framework\Message\Error::class); - $this->form->expects($this->once()) - ->method('validateData') - ->willReturn([$error]); - $validationResult = $this->getMockForAbstractClass( \Magento\Customer\Api\Data\ValidationResultsInterface::class, [], @@ -188,9 +177,6 @@ public function testExecute() public function testExecuteWithoutAddresses() { - $this->request->expects($this->once()) - ->method('getPost') - ->willReturn(null); $this->form->expects($this->once()) ->method('setInvisibleIgnored'); $this->form->expects($this->atLeastOnce()) @@ -223,9 +209,6 @@ public function testExecuteWithoutAddresses() public function testExecuteWithException() { - $this->request->expects($this->once()) - ->method('getPost') - ->willReturn(null); $this->form->expects($this->once()) ->method('setInvisibleIgnored'); $this->form->expects($this->atLeastOnce()) @@ -265,12 +248,6 @@ public function testExecuteWithException() public function testExecuteWithNewCustomerAndNoEntityId() { - $this->request->expects($this->once()) - ->method('getPost') - ->willReturn([ - '_template_' => null, - 'address_index' => null - ]); $this->request->expects($this->once()) ->method('getParam') ->with('customer') @@ -282,11 +259,6 @@ public function testExecuteWithNewCustomerAndNoEntityId() $this->form->expects($this->once())->method('setInvisibleIgnored'); $this->form->expects($this->atLeastOnce())->method('extractData')->willReturn([]); - $error = $this->createMock(\Magento\Framework\Message\Error::class); - $this->form->expects($this->once()) - ->method('validateData') - ->willReturn([$error]); - $validationResult = $this->getMockForAbstractClass( \Magento\Customer\Api\Data\ValidationResultsInterface::class, [], diff --git a/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php new file mode 100644 index 0000000000000..815a47288e370 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php @@ -0,0 +1,233 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Test\Unit\Model\Address; + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\ResourceModel\Address\CollectionFactory; +use Magento\Customer\Model\ResourceModel\Address\Collection as AddressCollection; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Type; +use Magento\Ui\DataProvider\EavValidationRules; +use Magento\Customer\Model\Attribute as AttributeModel; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Customer\Model\Address as AddressModel; +use Magento\Customer\Model\FileProcessorFactory; + +class DataProviderTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $addressCollectionFactory; + + /** + * @var AddressCollection|\PHPUnit_Framework_MockObject_MockObject + */ + private $collection; + + /** + * @var CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $customerRepository; + + /** + * @var CustomerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $customer; + + /** + * @var Config|\PHPUnit_Framework_MockObject_MockObject + */ + private $eavConfig; + + /** + * @var EavValidationRules|\PHPUnit_Framework_MockObject_MockObject + */ + private $eavValidationRules; + + /* + * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $context; + + /** + * @var \Magento\Customer\Model\Config\Share|\PHPUnit_Framework_MockObject_MockObject + */ + private $shareConfig; + + /** + * @var FileProcessorFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $fileProcessorFactory; + + /** + * @var Type|\PHPUnit_Framework_MockObject_MockObject + */ + private $entityType; + + /** + * @var AddressModel|\PHPUnit_Framework_MockObject_MockObject + */ + private $address; + + /** + * @var AttributeModel|\PHPUnit_Framework_MockObject_MockObject + */ + private $attribute; + + /** + * @var \Magento\Customer\Model\Address\DataProvider + */ + private $model; + + protected function setUp() + { + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->addressCollectionFactory = $this->getMockBuilder(CollectionFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->collection = $this->getMockBuilder(AddressCollection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->customerRepository = $this->getMockForAbstractClass(CustomerRepositoryInterface::class); + $this->eavValidationRules = $this->createMock(EavValidationRules::class); + $this->context = $this->getMockForAbstractClass(ContextInterface::class); + $this->fileProcessorFactory = $this->getMockBuilder(FileProcessorFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->shareConfig = $this->createMock(\Magento\Customer\Model\Config\Share::class); + $this->addressCollectionFactory->expects($this->once()) + ->method('create') + ->willReturn($this->collection); + $this->eavConfig = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->entityType = $this->getMockBuilder(Type::class) + ->disableOriginalConstructor() + ->getMock(); + $this->entityType->expects($this->once()) + ->method('getAttributeCollection') + ->willReturn([]); + $this->eavConfig->expects($this->once()) + ->method('getEntityType') + ->willReturn($this->entityType); + $this->customer = $this->getMockForAbstractClass(CustomerInterface::class); + $this->address = $this->getMockBuilder(AddressModel::class) + ->disableOriginalConstructor() + ->getMock(); + $this->attribute = $this->getMockBuilder(AttributeModel::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->model = $objectManagerHelper->getObject( + \Magento\Customer\Model\Address\DataProvider::class, + [ + '', + '', + '', + 'addressCollectionFactory' => $this->addressCollectionFactory, + 'customerRepository' => $this->customerRepository, + 'eavConfig' => $this->eavConfig, + 'eavValidationRules' => $this->eavValidationRules, + 'context' => $this->context, + 'fileProcessorFactory' => $this->fileProcessorFactory, + 'shareConfig' => $this->shareConfig, + [], + [], + true + ] + ); + } + + public function testGetDefaultData() + { + $expectedData = [ + '' => [ + 'parent_id' => 1, + 'firstname' => 'John', + 'lastname' => 'Doe' + ] + ]; + + $this->collection->expects($this->once()) + ->method('getItems') + ->willReturn([]); + + $this->context->expects($this->once()) + ->method('getRequestParam') + ->willReturn(1); + $this->customerRepository->expects($this->once()) + ->method('getById') + ->willReturn($this->customer); + $this->customer->expects($this->once()) + ->method('getFirstname') + ->willReturn('John'); + $this->customer->expects($this->once()) + ->method('getLastname') + ->willReturn('Doe'); + + $this->assertEquals($expectedData, $this->model->getData()); + } + + public function testGetData() + { + $expectedData = [ + '3' => [ + 'parent_id' => "1", + 'firstname' => 'John', + 'lastname' => 'Doe', + 'street' => [ + '42000 Ave W 55 Cedar City', + 'Apt. 33' + ] + ] + ]; + + $this->collection->expects($this->once()) + ->method('getItems') + ->willReturn([ + $this->address + ]); + + $this->customerRepository->expects($this->once()) + ->method('getById') + ->willReturn($this->customer); + $this->customer->expects($this->once()) + ->method('getDefaultBilling') + ->willReturn('1'); + $this->customer->expects($this->once()) + ->method('getDefaultShipping') + ->willReturn('1'); + + $this->address->expects($this->once()) + ->method('getEntityId') + ->willReturn('3'); + $this->address->expects($this->once()) + ->method('load') + ->with("3") + ->willReturnSelf(); + $this->address->expects($this->once()) + ->method('getData') + ->willReturn([ + 'parent_id' => "1", + 'firstname' => "John", + 'lastname' => 'Doe', + 'street' => "42000 Ave W 55 Cedar City\nApt. 33" + ]); + $this->address->expects($this->once()) + ->method('getAttributes') + ->willReturn([$this->attribute]); + $this->attribute->expects($this->once()) + ->method('getFrontendInput') + ->willReturn(null); + + $this->assertEquals($expectedData, $this->model->getData()); + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php new file mode 100644 index 0000000000000..eb1241de3e32c --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php @@ -0,0 +1,1065 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Test\Unit\Model\Customer; + +use Magento\Customer\Api\CustomerMetadataInterface; +use Magento\Customer\Model\Config\Share; +use Magento\Customer\Model\ResourceModel\Address\Attribute\Source\CountryWithWebsites; +use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Eav\Model\Entity\Type; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Ui\Component\Form\Field; +use Magento\Ui\DataProvider\EavValidationRules; + +/** + * Class DataProviderTest + * + * Test for class \Magento\Customer\Model\Customer\DataProviderWithDefaultAddresses + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class DataProviderWithDefaultAddressesTest extends \PHPUnit\Framework\TestCase +{ + const ATTRIBUTE_CODE = 'test-code'; + const OPTIONS_RESULT = 'test-options'; + + /** + * @var Config|\PHPUnit_Framework_MockObject_MockObject + */ + protected $eavConfigMock; + + /** + * @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $customerCollectionFactoryMock; + + /** + * @var EavValidationRules|\PHPUnit_Framework_MockObject_MockObject + */ + protected $eavValidationRulesMock; + + /** + * @var \Magento\Framework\Session\SessionManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $sessionMock; + + /** + * @var \Magento\Customer\Model\FileProcessorFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $fileProcessorFactory; + + /** + * @var \Magento\Customer\Model\FileProcessor|\PHPUnit_Framework_MockObject_MockObject + */ + protected $fileProcessor; + + /** + * @var \Magento\Directory\Model\CountryFactory + */ + private $countryFactoryMock; + + /** + * @var \Magento\Customer\Model\Customer + */ + private $customerMock; + + /** + * @var \Magento\Customer\Model\ResourceModel\Customer\Collection + */ + private $customerCollectionMock; + + /** + * @var \Magento\Customer\Model\Config\Share + */ + private $shareConfigMock; + + /** + * @var CountryWithWebsites + */ + private $countryWithWebsitesMock; + + /** + * Set up + * + * @return void + */ + protected function setUp() + { + $this->eavConfigMock = $this->getMockBuilder(\Magento\Eav\Model\Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->customerCollectionFactoryMock = $this->createPartialMock( + \Magento\Customer\Model\ResourceModel\Customer\CollectionFactory::class, + ['create'] + ); + $this->eavValidationRulesMock = $this + ->getMockBuilder(\Magento\Ui\DataProvider\EavValidationRules::class) + ->disableOriginalConstructor() + ->getMock(); + $this->sessionMock = $this + ->getMockBuilder(\Magento\Framework\Session\SessionManagerInterface::class) + ->setMethods(['getCustomerFormData', 'unsCustomerFormData']) + ->getMockForAbstractClass(); + + $this->fileProcessor = $this->getMockBuilder(\Magento\Customer\Model\FileProcessor::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->fileProcessorFactory = $this->getMockBuilder(\Magento\Customer\Model\FileProcessorFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->countryFactoryMock = $this->getMockBuilder(\Magento\Directory\Model\CountryFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create', 'loadByCode', 'getName']) + ->getMock(); + + $this->customerMock = $this->getMockBuilder(\Magento\Customer\Model\Customer::class) + ->disableOriginalConstructor() + ->getMock(); + $this->customerCollectionMock = $this->getMockBuilder( + \Magento\Customer\Model\ResourceModel\Customer\Collection::class + ) + ->disableOriginalConstructor() + ->getMock(); + $this->customerCollectionMock->expects($this->any())->method('addAttributeToSelect')->with('*'); + $this->customerCollectionFactoryMock + ->expects($this->any()) + ->method('create') + ->willReturn($this->customerCollectionMock); + + $this->eavConfigMock->expects($this->at(0)) + ->method('getEntityType') + ->with('customer') + ->willReturn($this->getTypeCustomerMock([])); + $this->eavConfigMock->expects($this->at(1)) + ->method('getEntityType') + ->with('customer_address') + ->willReturn($this->getTypeAddressMock()); + + $this->shareConfigMock = $this->getMockBuilder(\Magento\Customer\Model\Config\Share::class) + ->disableOriginalConstructor() + ->getMock(); + $this->countryWithWebsitesMock = $this->getMockBuilder(CountryWithWebsites::class) + ->disableOriginalConstructor() + ->setMethods(['getAllOptions']) + ->getMock(); + $this->countryWithWebsitesMock->expects($this->any())->method('getAllOptions')->willReturn('test-options'); + + $helper = new ObjectManager($this); + $this->dataProvider = $helper->getObject( + \Magento\Customer\Model\Customer\DataProviderWithDefaultAddresses::class, + [ + 'name' => 'test-name', + 'primaryFieldName' => 'primary-field-name', + 'requestFieldName' => 'request-field-name', + 'eavValidationRules' => $this->eavValidationRulesMock, + 'customerCollectionFactory' => $this->customerCollectionFactoryMock, + 'eavConfig' => $this->eavConfigMock, + 'countryFactory' => $this->countryFactoryMock, + 'session' => $this->sessionMock, + 'fileProcessorFactory' => $this->fileProcessorFactory, + 'shareConfig' => $this->shareConfigMock, + 'countryWithWebsites' => $this->countryWithWebsitesMock, + ] + ); + } + + /** + * Run test getAttributesMeta method + * + * @param array $expected + * @return void + * + * @dataProvider getAttributesMetaDataProvider + */ + public function testGetAttributesMetaWithOptions(array $expected) + { + $meta = $this->dataProvider->getMeta(); + $this->assertNotEmpty($meta); + $this->assertEquals($expected, $meta); + } + + /** + * Data provider for testGetAttributesMeta + * + * @return array + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function getAttributesMetaDataProvider() + { + return [ + [ + 'expected' => [ + 'customer' => [ + 'children' => [ + self::ATTRIBUTE_CODE => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'options' => 'test-options', + 'visible' => null, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + ], + ], + ], + ], + 'test-code-boolean' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'visible' => null, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + 'prefer' => 'toggle', + 'valueMap' => [ + 'true' => 1, + 'false' => 0, + ], + ], + ], + ], + ], + ], + ], + 'address' => [ + 'children' => [ + self::ATTRIBUTE_CODE => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'options' => 'test-options', + 'visible' => null, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + ], + ], + ], + ], + 'test-code-boolean' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'visible' => null, + 'required' => 'is_required', + 'label' => 'frontend_label', + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + 'prefer' => 'toggle', + 'valueMap' => [ + 'true' => 1, + 'false' => 0, + ], + ], + ], + ], + ], + 'country_id' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'options' => 'test-options', + 'visible' => null, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + 'filterBy' => [ + 'target' => '${ $.provider }:data.customer.website_id', + 'field' => 'website_ids' + ] + ], + ], + ], + ] + ], + ], + ] + ] + ]; + } + + /** + * @return CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected function getCustomerCollectionFactoryMock() + { + $collectionMock = $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Customer\Collection::class) + ->disableOriginalConstructor() + ->getMock(); + + $collectionMock->expects($this->any()) + ->method('addAttributeToSelect') + ->with('*'); + + $this->customerCollectionFactoryMock->expects($this->any()) + ->method('create') + ->willReturn($collectionMock); + + return $this->customerCollectionFactoryMock; + } + + /** + * @return Config|\PHPUnit_Framework_MockObject_MockObject + */ + protected function getEavConfigMock($customerAttributes = []) + { + $this->eavConfigMock->expects($this->at(0)) + ->method('getEntityType') + ->with('customer') + ->willReturn($this->getTypeCustomerMock($customerAttributes)); + $this->eavConfigMock->expects($this->at(1)) + ->method('getEntityType') + ->with('customer_address') + ->willReturn($this->getTypeAddressMock()); + + return $this->eavConfigMock; + } + + /** + * @return Type|\PHPUnit_Framework_MockObject_MockObject + */ + protected function getTypeCustomerMock($customerAttributes = []) + { + $typeCustomerMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Type::class) + ->disableOriginalConstructor() + ->getMock(); + $attributesCollection = !empty($customerAttributes) ? $customerAttributes : $this->getAttributeMock(); + $typeCustomerMock->expects($this->any()) + ->method('getEntityTypeCode') + ->willReturn('customer'); + foreach ($attributesCollection as $attribute) { + $attribute->expects($this->any()) + ->method('getEntityType') + ->willReturn($typeCustomerMock); + } + + $typeCustomerMock->expects($this->once()) + ->method('getAttributeCollection') + ->willReturn($attributesCollection); + + return $typeCustomerMock; + } + + /** + * @return Type|\PHPUnit_Framework_MockObject_MockObject + */ + protected function getTypeAddressMock() + { + $typeAddressMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Type::class) + ->disableOriginalConstructor() + ->getMock(); + + $typeAddressMock->expects($this->once()) + ->method('getAttributeCollection') + ->willReturn($this->getAttributeMock('address')); + + return $typeAddressMock; + } + + /** + * @param \PHPUnit_Framework_MockObject_MockObject $attributeMock + * @param \PHPUnit_Framework_MockObject_MockObject $attributeBooleanMock + * @param array $options + */ + private function injectVisibilityProps( + \PHPUnit_Framework_MockObject_MockObject $attributeMock, + \PHPUnit_Framework_MockObject_MockObject $attributeBooleanMock, + array $options = [] + ) { + if (isset($options[self::ATTRIBUTE_CODE]['visible'])) { + $attributeMock->expects($this->any()) + ->method('getIsVisible') + ->willReturn($options[self::ATTRIBUTE_CODE]['visible']); + } + + if (isset($options[self::ATTRIBUTE_CODE]['user_defined'])) { + $attributeMock->expects($this->any()) + ->method('getIsUserDefined') + ->willReturn($options[self::ATTRIBUTE_CODE]['user_defined']); + } + + if (isset($options[self::ATTRIBUTE_CODE]['is_used_in_forms'])) { + $attributeMock->expects($this->any()) + ->method('getUsedInForms') + ->willReturn($options[self::ATTRIBUTE_CODE]['is_used_in_forms']); + } + + if (isset($options['test-code-boolean']['visible'])) { + $attributeBooleanMock->expects($this->any()) + ->method('getIsVisible') + ->willReturn($options['test-code-boolean']['visible']); + } + + if (isset($options['test-code-boolean']['user_defined'])) { + $attributeBooleanMock->expects($this->any()) + ->method('getIsUserDefined') + ->willReturn($options['test-code-boolean']['user_defined']); + } + + if (isset($options['test-code-boolean']['is_used_in_forms'])) { + $attributeBooleanMock->expects($this->any()) + ->method('getUsedInForms') + ->willReturn($options['test-code-boolean']['is_used_in_forms']); + } + } + + /** + * @return AbstractAttribute[]|\PHPUnit_Framework_MockObject_MockObject[] + */ + protected function getAttributeMock($type = 'customer', $options = []) + { + $attributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) + ->setMethods( + [ + 'getAttributeCode', + 'getDataUsingMethod', + 'usesSource', + 'getFrontendInput', + 'getIsVisible', + 'getSource', + 'getIsUserDefined', + 'getUsedInForms', + 'getEntityType', + ] + ) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $sourceMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Source\AbstractSource::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $attributeCode = self::ATTRIBUTE_CODE; + if (isset($options[self::ATTRIBUTE_CODE]['specific_code_prefix'])) { + $attributeCode .= $options[self::ATTRIBUTE_CODE]['specific_code_prefix']; + } + + $attributeMock->expects($this->exactly(2)) + ->method('getAttributeCode') + ->willReturn($attributeCode); + + $sourceMock->expects($this->any()) + ->method('getAllOptions') + ->willReturn(self::OPTIONS_RESULT); + + $attributeMock->expects($this->any()) + ->method('getDataUsingMethod') + ->willReturnCallback($this->attributeGetUsingMethodCallback()); + + $attributeMock->expects($this->any()) + ->method('usesSource') + ->willReturn(true); + $attributeMock->expects($this->any()) + ->method('getSource') + ->willReturn($sourceMock); + + $attributeBooleanMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) + ->setMethods( + [ + 'getAttributeCode', + 'getDataUsingMethod', + 'usesSource', + 'getFrontendInput', + 'getIsVisible', + 'getIsUserDefined', + 'getUsedInForms', + 'getSource', + 'getEntityType', + ] + ) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $attributeBooleanMock->expects($this->any()) + ->method('getFrontendInput') + ->willReturn('boolean'); + $attributeBooleanMock->expects($this->any()) + ->method('getDataUsingMethod') + ->willReturnCallback($this->attributeGetUsingMethodCallback()); + + $attributeBooleanMock->expects($this->once()) + ->method('usesSource') + ->willReturn(false); + $booleanAttributeCode = 'test-code-boolean'; + if (isset($options['test-code-boolean']['specific_code_prefix'])) { + $booleanAttributeCode .= $options['test-code-boolean']['specific_code_prefix']; + } + + $attributeBooleanMock->expects($this->exactly(2)) + ->method('getAttributeCode') + ->willReturn($booleanAttributeCode); + + $this->eavValidationRulesMock->expects($this->any()) + ->method('build') + ->willReturnMap([ + [$attributeMock, $this->logicalNot($this->isEmpty()), []], + [$attributeBooleanMock, $this->logicalNot($this->isEmpty()), []], + ]); + $mocks = [$attributeMock, $attributeBooleanMock]; + $this->injectVisibilityProps($attributeMock, $attributeBooleanMock, $options); + if ($type == "address") { + $mocks[] = $this->getCountryAttrMock(); + } + return $mocks; + } + + /** + * Callback for ::getDataUsingMethod + * + * @return \Closure + */ + private function attributeGetUsingMethodCallback() + { + return function ($origName) { + return $origName; + }; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function getCountryAttrMock() + { + $objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); + $objectManagerMock->expects($this->any()) + ->method('get') + ->willReturnMap([ + [CountryWithWebsites::class, $this->countryWithWebsitesMock], + [Share::class, $this->shareConfigMock], + ]); + \Magento\Framework\App\ObjectManager::setInstance($objectManagerMock); + $countryAttrMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class) + ->setMethods(['getAttributeCode', 'getDataUsingMethod', 'usesSource', 'getSource', 'getLabel']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $countryAttrMock->expects($this->exactly(2)) + ->method('getAttributeCode') + ->willReturn('country_id'); + + $countryAttrMock->expects($this->any()) + ->method('getDataUsingMethod') + ->willReturnCallback( + function ($origName) { + return $origName; + } + ); + $countryAttrMock->expects($this->any()) + ->method('getLabel') + ->willReturn(__('frontend_label')); + $countryAttrMock->expects($this->any()) + ->method('usesSource') + ->willReturn(true); + $countryAttrMock->expects($this->any()) + ->method('getSource') + ->willReturn(null); + + return $countryAttrMock; + } + + /** + * @return void + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testGetData() + { + $customerData = [ + 'email' => 'test@test.ua', + 'default_billing' => 2, + 'default_shipping' => 2, + 'password_hash' => 'password_hash', + 'rp_token' => 'rp_token', + 'confirmation' => 'confirmation', + ]; + + $address = $this->getMockBuilder(\Magento\Customer\Model\Address::class)->disableOriginalConstructor() + ->getMock(); + $this->customerCollectionMock->expects($this->once())->method('getItems')->willReturn([$this->customerMock]); + $this->customerMock->expects($this->once())->method('getData')->willReturn($customerData); + $this->customerMock->expects($this->once())->method('getAttributes')->willReturn([]); + + $this->customerMock->expects($this->once())->method('getDefaultBillingAddress')->willReturn($address); + $this->countryFactoryMock->expects($this->once())->method('create')->willReturnSelf(); + $this->countryFactoryMock->expects($this->once())->method('loadByCode')->willReturnSelf(); + $this->countryFactoryMock->expects($this->once())->method('getName')->willReturn('Ukraine'); + + $this->sessionMock->expects($this->once()) + ->method('getCustomerFormData') + ->willReturn(null); + + $this->assertEquals( + [ + '' => [ + 'customer' => [ + 'email' => 'test@test.ua', + 'default_billing' => 2, + 'default_shipping' => 2, + ], + 'default_billing_address' => [ + 'country' => 'Ukraine', + ], + 'default_shipping_address' => [] + ] + ], + $this->dataProvider->getData() + ); + } + + /** + * @return void + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testGetDataWithCustomerFormData() + { + $customerId = 11; + $customerFormData = [ + 'customer' => [ + 'email' => 'test1@test1.ua', + 'default_billing' => 3, + 'default_shipping' => 3, + 'entity_id' => $customerId, + ], + 'address' => [ + 3 => [ + 'firstname' => 'firstname1', + 'lastname' => 'lastname1', + 'street' => [ + 'street1', + 'street2', + ], + 'default_billing' => 3, + 'default_shipping' => 3, + ], + ], + ]; + + $this->customerCollectionMock->expects($this->once())->method('getItems')->willReturn([$this->customerMock]); + $this->customerMock->expects($this->once()) + ->method('getData') + ->willReturn([ + 'email' => 'test@test.ua', + 'default_billing' => 2, + 'default_shipping' => 2, + ]); + $this->customerMock->expects($this->once())->method('getId')->willReturn($customerId); + $this->customerMock->expects($this->once())->method('getAttributes')->willReturn([]); + + $this->sessionMock->expects($this->once())->method('getCustomerFormData')->willReturn($customerFormData); + $this->sessionMock->expects($this->once())->method('unsCustomerFormData'); + + $this->assertEquals([$customerId => $customerFormData], $this->dataProvider->getData()); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return void + */ + public function testGetDataWithCustomAttributeImage() + { + $customerId = 1; + $customerEmail = 'user1@example.com'; + + $filename = '/filename.ext1'; + $viewUrl = 'viewUrl'; + $mime = 'image/png'; + + $expectedData = [ + $customerId => [ + 'customer' => [ + 'email' => $customerEmail, + 'img1' => [ + [ + 'file' => $filename, + 'size' => 1, + 'url' => $viewUrl, + 'name' => 'filename.ext1', + 'type' => $mime, + ], + ], + ], + 'default_billing_address' => [], + 'default_shipping_address' => [], + ], + ]; + + $attributeMock = $this->getMockBuilder(\Magento\Customer\Model\Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + $attributeMock->expects($this->exactly(2)) + ->method('getFrontendInput') + ->willReturn('image'); + $attributeMock->expects($this->exactly(2)) + ->method('getAttributeCode') + ->willReturn('img1'); + + $entityTypeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Type::class) + ->disableOriginalConstructor() + ->getMock(); + $entityTypeMock->expects($this->once()) + ->method('getEntityTypeCode') + ->willReturn(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + + $this->customerMock->expects($this->once()) + ->method('getData') + ->willReturn([ + 'email' => $customerEmail, + 'img1' => $filename, + ]); + $this->customerMock->expects($this->once())->method('getId')->willReturn($customerId); + $this->customerMock->expects($this->once())->method('getAttributes')->willReturn([$attributeMock]); + $this->customerMock->expects($this->once())->method('getEntityType')->willReturn($entityTypeMock); + $this->customerCollectionMock->expects($this->any())->method('getItems')->willReturn([$this->customerMock]); + $this->sessionMock->expects($this->once())->method('getCustomerFormData')->willReturn([]); + $this->fileProcessorFactory->expects($this->any()) + ->method('create') + ->with([ + 'entityTypeCode' => CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, + ]) + ->willReturn($this->fileProcessor); + $this->fileProcessor->expects($this->once())->method('isExist')->with($filename)->willReturn(true); + $this->fileProcessor->expects($this->once())->method('getStat')->with($filename)->willReturn(['size' => 1]); + $this->fileProcessor->expects($this->once())->method('getViewUrl') + ->with('/filename.ext1', 'image') + ->willReturn($viewUrl); + $this->fileProcessor->expects($this->once())->method('getMimeType')->with($filename)->willReturn($mime); + + $objectManager = new ObjectManager($this); + + $objectManager->setBackwardCompatibleProperty( + $this->dataProvider, + 'fileProcessorFactory', + $this->fileProcessorFactory + ); + + $this->assertEquals($expectedData, $this->dataProvider->getData()); + } + + public function testGetDataWithCustomAttributeImageNoData() + { + $customerId = 1; + $customerEmail = 'user1@example.com'; + + $expectedData = [ + $customerId => [ + 'customer' => [ + 'email' => $customerEmail, + 'img1' => [], + ], + 'default_billing_address' => [], + 'default_shipping_address' => [], + ], + ]; + + $attributeMock = $this->getMockBuilder(\Magento\Customer\Model\Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + $attributeMock->expects($this->once()) + ->method('getFrontendInput') + ->willReturn('image'); + $attributeMock->expects($this->exactly(2))->method('getAttributeCode') + ->willReturn('img1'); + + $entityTypeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Type::class) + ->disableOriginalConstructor() + ->getMock(); + $entityTypeMock->expects($this->once()) + ->method('getEntityTypeCode') + ->willReturn(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER); + + $this->customerMock->expects($this->once()) + ->method('getData') + ->willReturn([ + 'email' => $customerEmail, + ]); + $this->customerMock->expects($this->once())->method('getId')->willReturn($customerId); + $this->customerMock->expects($this->once())->method('getAttributes')->willReturn([$attributeMock]); + $this->customerMock->expects($this->once())->method('getEntityType')->willReturn($entityTypeMock); + $this->customerCollectionMock->expects($this->any())->method('getItems')->willReturn([$this->customerMock]); + $this->sessionMock->expects($this->once())->method('getCustomerFormData')->willReturn([]); + + $this->assertEquals($expectedData, $this->dataProvider->getData()); + } + + /** + * @return void + */ + public function testGetDataWithVisibleAttributes() + { + $firstAttributesBundle = $this->getAttributeMock( + 'customer', + [ + self::ATTRIBUTE_CODE => [ + 'visible' => true, + 'is_used_in_forms' => ['customer_account_edit'], + 'user_defined' => true, + 'specific_code_prefix' => "_1" + ], + 'test-code-boolean' => [ + 'visible' => true, + 'is_used_in_forms' => ['customer_account_create'], + 'user_defined' => true, + 'specific_code_prefix' => "_1" + ] + ] + ); + $secondAttributesBundle = $this->getAttributeMock( + 'customer', + [ + self::ATTRIBUTE_CODE => [ + 'visible' => true, + 'is_used_in_forms' => ['customer_account_create'], + 'user_defined' => false, + 'specific_code_prefix' => "_2" + ], + 'test-code-boolean' => [ + 'visible' => true, + 'is_used_in_forms' => ['customer_account_create'], + 'user_defined' => true, + 'specific_code_prefix' => "_2" + ] + ] + ); + + $helper = new ObjectManager($this); + /** @var \Magento\Customer\Model\Customer\DataProviderWithDefaultAddresses $dataProvider */ + $dataProvider = $helper->getObject( + \Magento\Customer\Model\Customer\DataProviderWithDefaultAddresses::class, + [ + 'name' => 'test-name', + 'primaryFieldName' => 'primary-field-name', + 'requestFieldName' => 'request-field-name', + 'eavValidationRules' => $this->eavValidationRulesMock, + 'customerCollectionFactory' => $this->getCustomerCollectionFactoryMock(), + 'eavConfig' => $this->getEavConfigMock(array_merge($firstAttributesBundle, $secondAttributesBundle)) + ] + ); + + $helper->setBackwardCompatibleProperty( + $dataProvider, + 'fileProcessorFactory', + $this->fileProcessorFactory + ); + + $meta = $dataProvider->getMeta(); + $this->assertNotEmpty($meta); + $this->assertEquals($this->getExpectationForVisibleAttributes(), $meta); + } + + /** + * Retrieve all customer variations of attributes with all variations of visibility + * + * @param bool $isRegistration + * @return array + */ + private function getCustomerAttributeExpectations($isRegistration) + { + return [ + self::ATTRIBUTE_CODE . "_1" => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'options' => 'test-options', + 'visible' => !$isRegistration, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + ], + ], + ], + ], + self::ATTRIBUTE_CODE . "_2" => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'options' => 'test-options', + 'visible' => true, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + ], + ], + ], + ], + 'test-code-boolean_1' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'visible' => $isRegistration, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + 'prefer' => 'toggle', + 'valueMap' => [ + 'true' => 1, + 'false' => 0, + ], + ], + ], + ], + ], + 'test-code-boolean_2' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'visible' => $isRegistration, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + 'prefer' => 'toggle', + 'valueMap' => [ + 'true' => 1, + 'false' => 0, + ], + ], + ], + ], + ], + ]; + } + + /** + * Retrieve all variations of attributes with all variations of visibility + * + * @param bool $isRegistration + * @return array + */ + private function getExpectationForVisibleAttributes($isRegistration = true) + { + return [ + 'customer' => [ + 'children' => $this->getCustomerAttributeExpectations($isRegistration), + ], + 'address' => [ + 'children' => [ + self::ATTRIBUTE_CODE => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'options' => 'test-options', + 'visible' => null, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + ], + ], + ], + ], + 'test-code-boolean' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'visible' => null, + 'required' => 'is_required', + 'label' => 'frontend_label', + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + 'prefer' => 'toggle', + 'valueMap' => [ + 'true' => 1, + 'false' => 0, + ], + ], + ], + ], + ], + 'country_id' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'dataType' => 'frontend_input', + 'formElement' => 'frontend_input', + 'options' => null, + 'visible' => null, + 'required' => 'is_required', + 'label' => __('frontend_label'), + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + 'componentType' => Field::NAME, + 'filterBy' => [ + 'target' => '${ $.provider }:data.customer.website_id', + 'field' => 'website_ids' + ] + ], + ], + ], + ] + ], + ], + ]; + } +} diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php index a142f87dcf6c4..1d262e7549873 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php @@ -195,35 +195,6 @@ public function testSave() $customerId = 1; $storeId = 2; - $region = $this->getMockForAbstractClass(\Magento\Customer\Api\Data\RegionInterface::class, [], '', false); - $address = $this->getMockForAbstractClass( - \Magento\Customer\Api\Data\AddressInterface::class, - [], - '', - false, - false, - true, - [ - 'setCustomerId', - 'setRegion', - 'getRegion', - 'getId' - ] - ); - $address2 = $this->getMockForAbstractClass( - \Magento\Customer\Api\Data\AddressInterface::class, - [], - '', - false, - false, - true, - [ - 'setCustomerId', - 'setRegion', - 'getRegion', - 'getId' - ] - ); $customerModel = $this->createPartialMock(\Magento\Customer\Model\Customer::class, [ 'getId', 'setId', @@ -243,10 +214,6 @@ public function testSave() $origCustomer = $this->customer; - $this->customer->expects($this->atLeastOnce()) - ->method('__toArray') - ->willReturn(['default_billing', 'default_shipping']); - $customerAttributesMetaData = $this->getMockForAbstractClass( \Magento\Framework\Api\CustomAttributesDataInterface::class, [], @@ -258,8 +225,6 @@ public function testSave() 'getId', 'getEmail', 'getWebsiteId', - 'getAddresses', - 'setAddresses' ] ); $customerSecureData = $this->createPartialMock(\Magento\Customer\Model\Data\CustomerSecure::class, [ @@ -287,28 +252,6 @@ public function testSave() $this->customerRegistry->expects($this->atLeastOnce()) ->method("remove") ->with($customerId); - $address->expects($this->once()) - ->method('setCustomerId') - ->with($customerId) - ->willReturnSelf(); - $address->expects($this->once()) - ->method('getRegion') - ->willReturn($region); - $address->expects($this->atLeastOnce()) - ->method('getId') - ->willReturn(7); - $address->expects($this->once()) - ->method('setRegion') - ->with($region); - $customerAttributesMetaData->expects($this->atLeastOnce()) - ->method('getAddresses') - ->willReturn([$address]); - $customerAttributesMetaData->expects($this->at(1)) - ->method('setAddresses') - ->with([]); - $customerAttributesMetaData->expects($this->at(2)) - ->method('setAddresses') - ->with([$address]); $this->extensibleDataObjectConverter->expects($this->once()) ->method('toNestedArray') ->with($customerAttributesMetaData, [], \Magento\Customer\Api\Data\CustomerInterface::class) @@ -393,12 +336,6 @@ public function testSave() $this->customerRegistry->expects($this->once()) ->method('push') ->with($customerModel); - $this->customer->expects($this->once()) - ->method('getAddresses') - ->willReturn([$address, $address2]); - $this->addressRepository->expects($this->once()) - ->method('save') - ->with($address); $customerAttributesMetaData->expects($this->once()) ->method('getEmail') ->willReturn('example@example.com'); @@ -446,41 +383,8 @@ public function testSaveWithPasswordHash() '', false ); - $address = $this->getMockForAbstractClass( - \Magento\Customer\Api\Data\AddressInterface::class, - [], - '', - false, - false, - true, - [ - 'setCustomerId', - 'setRegion', - 'getRegion', - 'getId' - ] - ); - $address2 = $this->getMockForAbstractClass( - \Magento\Customer\Api\Data\AddressInterface::class, - [], - '', - false, - false, - true, - [ - 'setCustomerId', - 'setRegion', - 'getRegion', - 'getId' - ] - ); - $origCustomer = $this->customer; - $this->customer->expects($this->atLeastOnce()) - ->method('__toArray') - ->willReturn(['default_billing', 'default_shipping']); - $customerModel = $this->createPartialMock(\Magento\Customer\Model\Customer::class, [ 'getId', 'setId', @@ -505,8 +409,6 @@ public function testSaveWithPasswordHash() 'getId', 'getEmail', 'getWebsiteId', - 'getAddresses', - 'setAddresses' ] ); $customerModel->expects($this->atLeastOnce()) @@ -559,28 +461,6 @@ public function testSaveWithPasswordHash() ->method('save') ->with($this->customer, CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, $this->customer) ->willReturn($customerAttributesMetaData); - $address->expects($this->once()) - ->method('setCustomerId') - ->with($customerId) - ->willReturnSelf(); - $address->expects($this->once()) - ->method('getRegion') - ->willReturn($region); - $address->expects($this->atLeastOnce()) - ->method('getId') - ->willReturn(7); - $address->expects($this->once()) - ->method('setRegion') - ->with($region); - $customerAttributesMetaData->expects($this->any()) - ->method('getAddresses') - ->willReturn([$address]); - $customerAttributesMetaData->expects($this->at(1)) - ->method('setAddresses') - ->with([]); - $customerAttributesMetaData->expects($this->at(2)) - ->method('setAddresses') - ->with([$address]); $customerAttributesMetaData ->expects($this->atLeastOnce()) ->method('getId') @@ -618,12 +498,6 @@ public function testSaveWithPasswordHash() $this->customerRegistry->expects($this->once()) ->method('push') ->with($customerModel); - $this->customer->expects($this->any()) - ->method('getAddresses') - ->willReturn([$address, $address2]); - $this->addressRepository->expects($this->once()) - ->method('save') - ->with($address); $customerAttributesMetaData->expects($this->once()) ->method('getEmail') ->willReturn('example@example.com'); diff --git a/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php b/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php new file mode 100644 index 0000000000000..82ce4249bcd85 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Ui\Test\Unit\Component\Form; + +use Magento\Customer\Ui\Component\Form\AddressFieldset; +use Magento\Framework\View\Element\UiComponent\ContextInterface; + +/** + * Test for class \Magento\Customer\Ui\Component\Form\AddressFieldset + */ +class AddressFieldsetTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var AddressFieldset + */ + protected $fieldset; + + /** + * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $context; + + /** + * Set up + * + * @return void + */ + protected function setUp() + { + $this->context = $this->getMockForAbstractClass( + \Magento\Framework\View\Element\UiComponent\ContextInterface::class + ); + $this->fieldset = new AddressFieldset( + $this->context, + [], + [] + ); + } + + /** + * Run test for canShow() method + * + * @return void + * + */ + public function testCanShow() + { + $this->context->expects($this->atLeastOnce())->method('getRequestParam')->with('id') + ->willReturn(1); + $this->assertEquals(true, $this->fieldset->canShow()); + } + + /** + * Run test for canShow() method without customer id in context + * + * @return void + * + */ + public function testCanShowWithoutId() + { + $this->context->expects($this->atLeastOnce())->method('getRequestParam')->with('id') + ->willReturn(null); + $this->assertEquals(false, $this->fieldset->canShow()); + } +} diff --git a/app/code/Magento/Customer/Ui/Component/Form/AddressFieldset.php b/app/code/Magento/Customer/Ui/Component/Form/AddressFieldset.php new file mode 100644 index 0000000000000..4d7464b321e85 --- /dev/null +++ b/app/code/Magento/Customer/Ui/Component/Form/AddressFieldset.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Ui\Component\Form; + +use Magento\Framework\View\Element\UiComponent\ContextInterface; + +/** + * Customer addresses fieldset class + */ +class AddressFieldset extends \Magento\Ui\Component\Form\Fieldset +{ + /** + * @param ContextInterface $context + * @param array $components + * @param array $data + */ + public function __construct( + ContextInterface $context, + array $components = [], + array $data = [] + ) { + $this->context = $context; + + parent::__construct($context, $components, $data); + } + + /** + * Can show customer addresses tab in tabs or not + * + * Will return false for not registered customer in a case when admin user created new customer account. + * Needed to hide addresses tab from create new customer page + * + * @return boolean + */ + public function canShow(): bool + { + $customerId = $this->context->getRequestParam('id'); + return (bool)$customerId; + } +} diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php new file mode 100644 index 0000000000000..75c02974ded8f --- /dev/null +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php @@ -0,0 +1,134 @@ +<?php +declare(strict_types=1); +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Ui\Component\Listing\Address\Column; + +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\View\Element\UiComponentFactory; +use Magento\Ui\Component\Listing\Columns\Column; +use Magento\Framework\UrlInterface; + +/** + * Prepare actions column for customer addresses grid + */ +class Actions extends Column +{ + const CUSTOMER_ADDRESS_PATH_DELETE = 'customer/address/delete'; + const CUSTOMER_ADDRESS_PATH_DEFAULT_SHIPPING = 'customer/address/defaultShippingAddress'; + const CUSTOMER_ADDRESS_PATH_DEFAULT_BILLING = 'customer/address/defaultBillingAddress'; + + /** + * @var UrlInterface + */ + protected $urlBuilder; + + /** + * @param ContextInterface $context + * @param UiComponentFactory $uiComponentFactory + * @param UrlInterface $urlBuilder + * @param array $components + * @param array $data + */ + public function __construct( + ContextInterface $context, + UiComponentFactory $uiComponentFactory, + UrlInterface $urlBuilder, + array $components = [], + array $data = [] + ) { + $this->urlBuilder = $urlBuilder; + parent::__construct($context, $uiComponentFactory, $components, $data); + } + + /** + * Prepare Data Source + * + * @param array $dataSource + * @return array + */ + public function prepareDataSource(array $dataSource): array + { + if (isset($dataSource['data']['items'])) { + foreach ($dataSource['data']['items'] as &$item) { + $name = $this->getData('name'); + if (isset($item['entity_id'])) { + $item[$name]['edit'] = [ + 'callback' => [ + [ + 'provider' => 'customer_form.areas.address.address' + . '.customer_address_update_modal.update_customer_address_form_loader', + 'target' => 'destroyInserted', + ], + [ + 'provider' => 'customer_form.areas.address.address' + . '.customer_address_update_modal', + 'target' => 'openModal', + ], + [ + 'provider' => 'customer_form.areas.address.address' + . '.customer_address_update_modal.update_customer_address_form_loader', + 'target' => 'render', + 'params' => [ + 'entity_id' => $item['entity_id'], + ], + ] + ], + 'href' => '#', + 'label' => __('Edit'), + 'hidden' => false, + ]; + + $item[$name]['set_default_shipping'] = [ + 'href' => $this->urlBuilder->getUrl( + self::CUSTOMER_ADDRESS_PATH_DEFAULT_SHIPPING, + ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] + ), + 'label' => __('Set as default shipping'), + 'confirm' => [ + 'title' => __('Set address as default shipping'), + 'message' => __( + 'Are you sure you want to set the address with ID: %1 as default shipping address?', + $item['entity_id'] + ) + ] + ]; + + $item[$name]['set_default_billing'] = [ + 'href' => $this->urlBuilder->getUrl( + self::CUSTOMER_ADDRESS_PATH_DEFAULT_BILLING, + ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] + ), + 'label' => __('Set as default billing'), + 'confirm' => [ + 'title' => __('Set address as default billing'), + 'message' => __( + 'Are you sure you want to set the address with ID: %1 as default billing address?', + $item['entity_id'] + ) + ] + ]; + + $item[$name]['delete'] = [ + 'href' => $this->urlBuilder->getUrl( + self::CUSTOMER_ADDRESS_PATH_DELETE, + ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] + ), + 'label' => __('Delete'), + 'confirm' => [ + 'title' => __('Delete address'), + 'message' => __( + 'Are you sure you want to delete the address with ID: %1?', + $item['entity_id'] + ) + ] + ]; + } + } + } + + return $dataSource; + } +} diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php new file mode 100644 index 0000000000000..438a16ec3ba55 --- /dev/null +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php @@ -0,0 +1,37 @@ +<?php + +namespace Magento\Customer\Ui\Component\Listing\Address\Column; + +use Magento\Framework\Data\OptionSourceInterface; + +/** + * Class for process countries in customer addresses grid + */ +class Countries implements OptionSourceInterface +{ + /** + * @var \Magento\Directory\Model\ResourceModel\Country\CollectionFactory + */ + private $countryCollectionFactory; + + /** + * @param \Magento\Directory\Model\ResourceModel\Country\CollectionFactory $collectionFactory + */ + public function __construct( + \Magento\Directory\Model\ResourceModel\Country\CollectionFactory $collectionFactory + ) { + $this->countryCollectionFactory = $collectionFactory; + } + + /** + * Get list of countries with country id as value and code as label + * + * @return array + */ + public function toOptionArray(): array + { + /** @var \Magento\Directory\Model\ResourceModel\Country\Collection $countryCollection */ + $countryCollection = $this->countryCollectionFactory->create(); + return $countryCollection->toOptionArray(); + } +} diff --git a/app/code/Magento/Customer/etc/db_schema.xml b/app/code/Magento/Customer/etc/db_schema.xml index 7971627521740..7e0b911e26184 100644 --- a/app/code/Magento/Customer/etc/db_schema.xml +++ b/app/code/Magento/Customer/etc/db_schema.xml @@ -120,6 +120,15 @@ <index referenceId="CUSTOMER_ADDRESS_ENTITY_PARENT_ID" indexType="btree"> <column name="parent_id"/> </index> + <index name="CUSTOMER_ADDRESS_ENTITY_FULLTEXT_INDEX" indexType="fulltext"> + <column name="firstname"/> + <column name="lastname"/> + <column name="street"/> + <column name="city"/> + <column name="region"/> + <column name="postcode"/> + <column name="telephone"/> + </index> </table> <table name="customer_address_entity_datetime" resource="default" engine="innodb" comment="Customer Address Entity Datetime"> diff --git a/app/code/Magento/Customer/etc/di.xml b/app/code/Magento/Customer/etc/di.xml index 6e8c3dc68ed28..a63bff59f1dcf 100644 --- a/app/code/Magento/Customer/etc/di.xml +++ b/app/code/Magento/Customer/etc/di.xml @@ -223,6 +223,7 @@ <item name="customer_listing_data_source" xsi:type="string">Magento\Customer\Model\ResourceModel\Grid\Collection</item> <item name="customer_online_grid_data_source" xsi:type="string">Magento\Customer\Model\ResourceModel\Online\Grid\Collection</item> <item name="customer_group_listing_data_source" xsi:type="string">Magento\Customer\Model\ResourceModel\Group\Grid\Collection</item> + <item name="customer_address_listing_data_source" xsi:type="string">Magento\Customer\Model\ResourceModel\Address\Grid\Collection</item> </argument> </arguments> </type> @@ -449,6 +450,14 @@ <argument name="resourceModel" xsi:type="string">Magento\Customer\Model\ResourceModel\Group</argument> </arguments> </type> + <type name="Magento\Customer\Model\ResourceModel\Address\Grid\Collection"> + <arguments> + <argument name="mainTable" xsi:type="string">customer_address_entity</argument> + <argument name="eventPrefix" xsi:type="string">customer_address_entity_grid_collection</argument> + <argument name="eventObject" xsi:type="string">customer_address_entity_grid_collection</argument> + <argument name="resourceModel" xsi:type="string">Magento\Customer\Model\ResourceModel\Address</argument> + </arguments> + </type> <preference for="Magento\Customer\Api\AccountDelegationInterface" type="Magento\Customer\Model\Delegation\AccountDelegation" /> diff --git a/app/code/Magento/Customer/view/adminhtml/layout/customer_address_edit.xml b/app/code/Magento/Customer/view/adminhtml/layout/customer_address_edit.xml new file mode 100644 index 0000000000000..3acae3acec8aa --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/layout/customer_address_edit.xml @@ -0,0 +1,15 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <update handle="styles"/> + <body> + <referenceContainer name="content"> + <uiComponent name="customer_address_form"/> + </referenceContainer> + </body> +</page> diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml new file mode 100644 index 0000000000000..a628c1b1b0b1e --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -0,0 +1,230 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> + <argument name="data" xsi:type="array"> + <item name="js_config" xsi:type="array"> + <item name="provider" xsi:type="string">customer_address_form.customer_address_form_data_source</item> + </item> + <item name="label" xsi:type="string" translate="true">Update Address</item> + <item name="reverseMetadataMerge" xsi:type="boolean">true</item> + <item name="template" xsi:type="string">templates/form/collapsible</item> + </argument> + <settings> + <buttons> + <button name="cancel" class="Magento\Customer\Block\Adminhtml\Edit\Address\CancelButton"/> + <button name="delete" class="Magento\Customer\Block\Adminhtml\Edit\Address\DeleteButton"/> + <button name="save" class="Magento\Customer\Block\Adminhtml\Edit\Address\SaveButton"/> + </buttons> + <namespace>customer_address_form</namespace> + <dataScope>data</dataScope> + <deps> + <dep>customer_address_form.customer_address_form_data_source</dep> + </deps> + </settings> + <dataSource name="customer_address_form_data_source"> + <argument name="data" xsi:type="array"> + <item name="js_config" xsi:type="array"> + <item name="component" xsi:type="string">Magento_Ui/js/form/provider</item> + </item> + </argument> + <settings> + <submitUrl path="customer/address/save"/> + <validateUrl path="customer/address/validate"/> + </settings> + <aclResource>Magento_Customer::manage</aclResource> + <dataProvider class="Magento\Customer\Model\Address\DataProvider" name="customer_address_form_data_source"> + <settings> + <requestFieldName>entity_id</requestFieldName> + <primaryFieldName>entity_id</primaryFieldName> + </settings> + </dataProvider> + </dataSource> + <fieldset name="general"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="is_collection" xsi:type="boolean">true</item> + </item> + </argument> + <settings> + <label/> + <dataScope/> + </settings> + + <field name="entity_id" formElement="hidden"> + <settings> + <dataType>text</dataType> + </settings> + </field> + <field name="default_shipping" sortOrder="0" formElement="checkbox"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="default" xsi:type="number">0</item> + </item> + </argument> + <settings> + <dataType>boolean</dataType> + <label translate="true">Default Shipping Address</label> + <dataScope>default_shipping</dataScope> + </settings> + <formElements> + <checkbox> + <settings> + <valueMap> + <map name="false" xsi:type="number">0</map> + <map name="true" xsi:type="number">1</map> + </valueMap> + <prefer>toggle</prefer> + </settings> + </checkbox> + </formElements> + </field> + <field name="default_billing" sortOrder="1" formElement="checkbox"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="default" xsi:type="number">0</item> + </item> + </argument> + <settings> + <dataType>boolean</dataType> + <label translate="true">Default Billing Address</label> + <dataScope>default_billing</dataScope> + </settings> + <formElements> + <checkbox> + <settings> + <valueMap> + <map name="false" xsi:type="number">0</map> + <map name="true" xsi:type="number">1</map> + </valueMap> + <prefer>toggle</prefer> + </settings> + </checkbox> + </formElements> + </field> + <field name="prefix" sortOrder="10" formElement="input"> + <settings> + <dataType>text</dataType> + <visible>true</visible> + <label translate="true">Name Prefix</label> + </settings> + </field> + <field name="firstname" sortOrder="20" formElement="input"> + <settings> + <dataType>text</dataType> + <visible>true</visible> + <label translate="true">First Name</label> + <validation> + <rule name="required-entry" xsi:type="boolean">true</rule> + </validation> + </settings> + </field> + <field name="lastname" sortOrder="30" formElement="input"> + <settings> + <dataType>text</dataType> + <visible>true</visible> + <label translate="true">Last Name</label> + <validation> + <rule name="required-entry" xsi:type="boolean">true</rule> + </validation> + </settings> + </field> + <field name="suffix" sortOrder="40" formElement="input"> + <settings> + <dataType>text</dataType> + <visible>true</visible> + <label translate="true">Name Suffix</label> + </settings> + </field> + <field name="middlename" sortOrder="50" formElement="input"> + <settings> + <dataType>text</dataType> + <visible>true</visible> + <label translate="true">Middle Name/Initial</label> + </settings> + </field> + <field name="company" sortOrder="60" formElement="input"> + <settings> + <dataType>text</dataType> + <visible>true</visible> + <label translate="true">Company</label> + </settings> + </field> + <field name="city" sortOrder="80" formElement="input"> + <settings> + <dataType>text</dataType> + <label translate="true">City</label> + <visible>true</visible> + <validation> + <rule name="required-entry" xsi:type="boolean">true</rule> + </validation> + </settings> + </field> + <field name="country_id" component="Magento_Ui/js/form/element/country" sortOrder="90" formElement="select"> + <settings> + <validation> + <rule name="required-entry" xsi:type="boolean">true</rule> + </validation> + <dataType>text</dataType> + </settings> + <formElements> + <select> + <settings> + <options class="Magento\Directory\Model\ResourceModel\Country\Collection"/> + </settings> + </select> + </formElements> + </field> + <field name="region_id" component="Magento_Ui/js/form/element/region" formElement="select"> + <settings> + <validation> + <rule name="required-entry" xsi:type="boolean">true</rule> + </validation> + <dataType>text</dataType> + <label translate="true">State/Province</label> + </settings> + <formElements> + <select> + <settings> + <filterBy> + <field>country_id</field> + <target>${ $.provider }:${ $.parentScope }.country_id</target> + </filterBy> + <customEntry>region</customEntry> + <options class="Magento\Directory\Model\ResourceModel\Region\Collection"/> + </settings> + </select> + </formElements> + </field> + <field name="postcode" sortOrder="120" formElement="input"> + <settings> + <dataType>text</dataType> + <visible>true</visible> + <label translate="true">Zip/Postal Code</label> + <validation> + <rule name="required-entry" xsi:type="boolean">true</rule> + </validation> + </settings> + </field> + <field name="telephone" sortOrder="130" formElement="input"> + <settings> + <dataType>text</dataType> + <visible>true</visible> + <label translate="true">Phone Number</label> + </settings> + </field> + <field name="vat_id" sortOrder="140" formElement="input"> + <settings> + <dataType>text</dataType> + <label translate="true">VAT Number</label> + <validation> + <rule name="validate-number" xsi:type="boolean">true</rule> + </validation> + </settings> + </field> + </fieldset> +</form> diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml new file mode 100644 index 0000000000000..cfc62fc99b10e --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml @@ -0,0 +1,171 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> + <argument name="data" xsi:type="array"> + <item name="js_config" xsi:type="array"> + <item name="provider" xsi:type="string">customer_address_listing.customer_address_listing_data_source</item> + </item> + </argument> + <settings> + <spinner>customer_address_columns</spinner> + <deps> + <dep>customer_address_listing.customer_address_listing_data_source</dep> + </deps> + </settings> + <dataSource name="customer_address_listing_data_source" component="Magento_Ui/js/grid/provider"> + <settings> + <filterUrlParams> + <param name="id">*</param> + </filterUrlParams> + <storageConfig> + <param name="indexField" xsi:type="string">entity_id</param> + </storageConfig> + <updateUrl path="mui/index/render"/> + </settings> + <aclResource>Magento_Customer::manage</aclResource> + <dataProvider class="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider" name="customer_address_listing_data_source"> + <settings> + <requestFieldName>id</requestFieldName> + <primaryFieldName>entity_id</primaryFieldName> + </settings> + </dataProvider> + </dataSource> + <listingToolbar name="listing_top"> + <bookmark name="bookmarks"/> + <columnsControls name="columns_controls"/> + <!-- Filter Search --> + <filterSearch name="fulltext"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="provider" xsi:type="string">customer_address_listing.customer_address_listing_data_source</item> + <item name="chipsProvider" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.listing_filters_chips</item> + <item name="storageConfig" xsi:type="array"> + <item name="provider" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.bookmarks</item> + <item name="namespace" xsi:type="string">current.search</item> + </item> + </item> + </argument> + </filterSearch> + <filters name="listing_filters"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="storageConfig" xsi:type="array"> + <item name="provider" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.bookmarks</item> + <item name="namespace" xsi:type="string">current.filters</item> + </item> + <item name="childDefaults" xsi:type="array"> + <item name="provider" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.listing_filters</item> + <item name="imports" xsi:type="array"> + <item name="visible" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.bookmarks:current.columns.${ $.index }.visible</item> + </item> + </item> + </item> + </argument> + </filters> + <massaction name="listing_massaction" component="Magento_Ui/js/grid/tree-massactions"> + <action name="delete"> + <settings> + <confirm> + <message translate="true">Are you sure to delete selected address?</message> + <title translate="true">Delete items + + + delete + + + + + + + + + + + + false + + entity_id + true + customer_address_listing.customer_address_listing.address_columns.ids + + + + customer_address_listing.customer_address_listing.address_columns_editor + startEdit + + ${ $.$data.rowIndex } + true + + + + + + + entity_id + + + + + text + + + + + + text + + + + + + text + + + + + + text + + + + + + text + + + + + + text + + + + + + select + + select + + + + + + text + + text + + + + + + + entity_id + + + + diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address-block.js b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address-block.js new file mode 100644 index 0000000000000..a715aae1ebd96 --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address-block.js @@ -0,0 +1,17 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'uiElement' +], function($, Component) { + 'use strict'; + + return Component.extend({ + defaults: { + template: 'Magento_Customer/default-address' + } + }); +}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js new file mode 100644 index 0000000000000..3a1500e5dc99e --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js @@ -0,0 +1,44 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'Magento_Ui/js/form/components/button' +], function (Button) { + 'use strict'; + + return Button.extend({ + initialize: function () { + this._super(); + if (!this.parent_id) { + this.visible(this.entity_id); + } + }, + + defaults: { + entity_id: null, + parent_id: null + }, + + /** + * Apply action on target component, + * but previously create this component from template if it is not existed + * + * @param {Object} action - action configuration + */ + applyAction: function (action) { + if (action.params && action.params[0]) { + action.params[0].entity_id = this.entity_id; + action.params[0].parent_id = this.parent_id; + } else { + action.params = [{ + entity_id: this.entity_id, + parent_id: this.parent_id + }]; + } + + this._super(); + } + }); +}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/address/modal.js b/app/code/Magento/Customer/view/adminhtml/web/js/address/modal.js new file mode 100644 index 0000000000000..94701f4c1af99 --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/address/modal.js @@ -0,0 +1,203 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'jquery', + 'Magento_Ui/js/modal/modal-component', + 'uiRegistry', + 'underscore' +], function ($, Modal, registry, _) { + 'use strict'; + + return Modal.extend({ + defaults: { + modules: { + emailProvider: '${ $.emailProvider }' + } + }, + + /** + * Initializes component. + * + * @returns {Object} Chainable. + */ + initialize: function () { + this._super(); + + // console.log(this.name); + + return this; + }, + + /** + * Open modal. + */ + openModal: function (data) { + debugger; + if (data == null){ + // add + this.setTitle(this.options.title); + this._super(); + } else { + // edit + var addressId = data.uuid; + var address = { + 'city': 'city', + 'company': 'company', + 'country_id': 'country_id', + 'customer_id': 'customer_id', + 'created_at': 'created_at', + 'default_billing': 'default_billing', + 'default_shipping': 'default_shipping', + 'entity_id': 'entity_id', + 'fax': 'fax', + 'firstname': 'firstname', + 'increment_id': 'increment_id', + 'is_active': 'is_active', + 'lastname': 'lastname', + 'middlename': 'middlename', + 'parent_id': 'parent_id', + 'postcode': 'postcode', + 'prefix': 'prefix', + 'region': 'region', + 'region_id': 'region_id', + 'street': [0, 1], + 'suffix': 'suffix', + 'telephone': 'telephone', + 'updated_at': 'updated_at', + 'vat_id': 'vat_id', + 'vat_is_valid': 'vat_is_valid', + 'vat_request_date': 'vat_request_date', + 'vat_request_id': 'vat_request_id', + 'vat_request_success': 'vat_request_success' + }; + + var source = registry.get('customer_form.customer_form_data_source'); + var modal = 'data.address_listing.address_form.update_customer_address_form_modal'; + + _.each(address, function(value, key) { + if (key === 'default_billing' || key === 'default_shipping') { + var defaultValue = source.get('data.address.' + addressId + '.' + value); + // convert boolean to integer + var val = +defaultValue; + source.set(modal + '.' + key, val.toString()); + } else if (key === 'street' && _.isArray(address[key])) { + _.each(address[key], function(element, index) { + source.set(modal + '.' + key + '[' + index + ']', source.get('data.address.' + addressId + '.' + key + '.' + element)); + }); + } else { + source.set(modal + '.' + key, source.get('data.address.' + addressId + '.' + value)); + } + }); + + this.setTitle(this.options.title); + this._super(); + } + }, + + /** + * Close popup modal. + * @public + */ + closeModal: function () { + debugger; + this._clearData(); + this._super(); + }, + + /** + * Clear modal data. + * + * @private + */ + _clearData: function () { + debugger; + var address = { + 'city': '', + 'company': '', + 'country_id': '', + 'default_billing': "0", + 'default_shipping': "0", + 'entity_id': '', + 'firstname': '', + 'is_active': '', + 'lastname': '', + 'middlename': '', + 'postcode': '', + 'prefix': '', + 'region': '', + 'region_id': '', + 'street[0]': '', + 'street[1]': '', + 'suffix': '', + 'telephone': '', + 'vat_id': '' + }; + + var source = registry.get('customer_form.customer_form_data_source'); + var modal = 'data.address_listing.address_form.update_customer_address_form_modal'; + + _.each(address, function(value, key) { + source.set(modal + '.' + key, value); + }); + }, + + /** + * Open modal. + */ + save: function () { + debugger; + + var address = { + 'city': 'city', + 'company': 'company', + 'country_id': 'country_id', + 'customer_id': 'customer_id', + 'created_at': 'created_at', + 'default_billing': 'default_billing', + 'default_shipping': 'default_shipping', + 'entity_id': 'entity_id', + 'fax': 'fax', + 'firstname': 'firstname', + 'increment_id': 'increment_id', + 'is_active': 'is_active', + 'lastname': 'lastname', + 'middlename': 'middlename', + 'parent_id': 'parent_id', + 'postcode': 'postcode', + 'prefix': 'prefix', + 'region': 'region', + 'region_id': 'region_id', + 'street': ['street[0]', 'street[1]'], + 'suffix': 'region_id', + 'telephone': 'telephone', + 'updated_at': 'updated_at', + 'vat_id': 'vat_id', + 'vat_is_valid': 'vat_is_valid', + 'vat_request_date': 'vat_request_date', + 'vat_request_id': 'vat_request_id', + 'vat_request_success': 'vat_request_success' + }; + + var source = registry.get('customer_form.customer_form_data_source'); + var formData = source.get('data.address_listing.address_form.update_customer_address_form_modal'); + var entityId = formData.entity_id; + + $.ajax({ + url: this.options.url, + showLoader: true, + data: formData, + type: "POST", + dataType: 'json', + success: function(data) { + console.log('SUCCESS: ', data); + }, + error: function(data) { + console.log('ERROR: ', data); + } + }); + } + }); +}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js new file mode 100644 index 0000000000000..edfb004654a9b --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js @@ -0,0 +1,164 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'Magento_Ui/js/form/components/insert-form' +], function (Insert) { + 'use strict'; + + return Insert.extend({ + // Should be refactored after form save.!!!!! + // defaults: { + // updateModalProvider: '${ $.parentName }', + // subTitlePrefix: $t('Belongs to '), + // switcherSelector: '.store-switcher', + // toRemove: [], + // // imports: { + // // removeResponseData: '${ $.removeResponseProvider }', + // // modalTitle: '${ $.modalTitleProvider }', + // // modalSubTitle: '${ $.modalSubTitleProvider }', + // // destroyClosedModalContents: '${ $.updateModalProvider }:state' + // // }, + // // listens: { + // // responseData: 'afterUpdate', + // // removeResponseData: 'afterRemove', + // // modalTitle: 'changeModalTitle', + // // modalSubTitle: 'changeModalSubTitle' + // // }, + // modules: { + // updateModal: '${ $.updateModalProvider }', + // removeModal: '${ $.removeModalProvider }', + // upcomingListing: 'index = ${ $.upcomingListingProvider }' + // } + // }, + // + // /** @inheritdoc **/ + // initialize: function () { + // _.bindAll(this, 'onSwitcherSelect'); + // this._super(); + // this.updateModal(this.initSwitcherHandler.bind(this)); + // + // return this; + // }, + // + // initConfig: function (options) { + // debugger; + // return this._super(); + // }, + // + // /** @inheritdoc */ + // destroyInserted: function () { + // if (this.isRendered) { + // _.each(this.toRemove, function (componentName) { + // registry.get(componentName, function (component) { + // if (component.hasOwnProperty('delegate')) { + // component.delegate('destroy'); + // } else { + // component.destroy(); + // } + // }); + // }); + // } + // + // this._super(); + // }, + // + // // /** + // // * Form save callback. + // // * + // // * @param {Object} data + // // */ + // // afterUpdate: function (data) { + // // if (!data.error) { + // // this.updateModal('closeModal'); + // // this.upcomingListing('reload'); + // // } + // // }, + // + // // /** + // // * Form remove callback. + // // * + // // * @param {Object} data + // // */ + // // afterRemove: function (data) { + // // if (!data.error) { + // // this.removeModal('closeModal'); + // // this.afterUpdate(data); + // // } + // // }, + // + // // /** + // // * Change modal title. + // // * + // // * @param {String} title + // // */ + // // changeModalTitle: function (title) { + // // this.updateModal('setTitle', title); + // // }, + // // + // // /** + // // * Change modal sub title. + // // * + // // * @param {String} subTitle + // // */ + // // changeModalSubTitle: function (subTitle) { + // // subTitle = subTitle ? + // // this.subTitlePrefix + this.modalTitle + ' ' + subTitle : + // // ''; + // // + // // this.updateModal('setSubTitle', subTitle); + // // }, + // + // /** + // * Destroy contents of modal when it is closed + // * + // * @param {Boolean} state + // */ + // destroyClosedModalContents: function (state) { + // if (state === false) { + // this.destroyInserted(); + // } + // }, + // + // /** + // * Switcher initialization. + // */ + // initSwitcherHandler: function () { + // var switcherSelector = this.updateModal().rootSelector + ' ' + this.switcherSelector, + // self = this; + // + // $.async(switcherSelector, function (switcher) { + // $(switcher).on('click', 'li a', self.onSwitcherSelect); + // }); + // }, + // + // /** + // * Store switcher selection handler. + // * @param {Object} e - event object. + // */ + // onSwitcherSelect: function (e) { + // var self = this, + // param = $(e.currentTarget).data('param'), + // params = { + // store: 0 + // }; + // + // params[param] = $(e.currentTarget).data('value'); + // + // uiConfirm({ + // content: $t('Please confirm scope switching. All data that hasn\'t been saved will be lost.'), + // actions: { + // + // /** Confirm callback. */ + // confirm: function () { + // self.destroyInserted(); + // params = _.extend(self.previousParams, params); + // self.render(params); + // } + // } + // }); + // } + }); +}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html new file mode 100644 index 0000000000000..1ee5a4da81d2b --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html @@ -0,0 +1,42 @@ +
+
+
+
+ +
+
+ + + + + +
+ + +
+
+ + +
+
+ + + + +
+
+ + +
T: +
+ +
F: +
+ +
VAT: +
+
+ +
+
+
diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index d2a9b3f44624d..1eb3652628c28 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -43,7 +43,7 @@ - + id entity_id @@ -303,262 +303,175 @@ -
- - - true - Are you sure you want to delete this item? - - +
+ false + fieldset + + true + - - - - address - - - - number - false - - - + - address + billing-address + Default Billing Address + customer-default-billing-address-content + The customer does not have default billing address - text - true - - - - - - address - - - - - true - - text - ${ $.provider }:data.customer.firstname + ${ $.provider}:data.default_billing_address - - - - - address - - - - text - true - - - - - - address + + + + + - address + shipping-address + Default Shipping Address + customer-default-shipping-address-content + The customer does not have default shipping address - - true - - text - - - - - - address - - - - - true - - text - ${ $.provider }:data.customer.website_id + ${ $.provider}:data.default_shipping_address - - - - - address - - - - text - false - - - - - - address - - - - - true - - text - - - - - - - - - address - - - - - true - - text - - - - - - address - - - - text - - - - - - address - - - - - 0 - - text - - - - - - address + + + + + - ui/form/element/checkbox - boolean + + + - - - - Default Shipping Address - - - - + + + + + customer_address_edit + 1 + + false + ${ $.parentName } + ${ $.ns }.customer_address_form_data_source + customer_address_form + + + + + + + false + true + + customer_address_listing.customer_address_listing_data_source + customer_address_listing.customer_address_listing.customer_address_listing_columns.ids + true + customer_address_listing + customer_address_listing + + ${ $.externalProvider }:params.parent_id + + + ${ $.provider }:data.customer.entity_id + + +
diff --git a/app/code/Magento/Ui/Component/Form/Element/DataType/Media.php b/app/code/Magento/Ui/Component/Form/Element/DataType/Media.php index c2460fd8385c1..f15a97e96c549 100644 --- a/app/code/Magento/Ui/Component/Form/Element/DataType/Media.php +++ b/app/code/Magento/Ui/Component/Form/Element/DataType/Media.php @@ -35,6 +35,7 @@ public function prepare() $this->getData(), [ 'config' => [ + 'dataScope' => $this->getName(), 'uploaderConfig' => [ 'url' => $url ], diff --git a/app/code/Magento/Ui/Component/Form/Fieldset.php b/app/code/Magento/Ui/Component/Form/Fieldset.php index 745068ca0fa8a..ebfe58f3abb89 100644 --- a/app/code/Magento/Ui/Component/Form/Fieldset.php +++ b/app/code/Magento/Ui/Component/Form/Fieldset.php @@ -5,11 +5,7 @@ */ namespace Magento\Ui\Component\Form; -use Magento\Ui\Component\Container; use Magento\Ui\Component\AbstractComponent; -use Magento\Framework\View\Element\UiComponentFactory; -use Magento\Framework\View\Element\UiComponentInterface; -use Magento\Framework\View\Element\UiComponent\ContextInterface; /** * @api @@ -33,4 +29,14 @@ public function getComponentName() { return static::NAME; } + + /** + * Check that fieldset can be shown. + * + * @return bool + */ + public function canShow(): bool + { + return true; + } } diff --git a/app/code/Magento/Ui/Component/Layout/Tabs.php b/app/code/Magento/Ui/Component/Layout/Tabs.php index 8ceac716ae218..02e8979f525ef 100644 --- a/app/code/Magento/Ui/Component/Layout/Tabs.php +++ b/app/code/Magento/Ui/Component/Layout/Tabs.php @@ -11,6 +11,7 @@ use Magento\Framework\View\Element\UiComponent\LayoutInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Framework\View\Element\UiComponentInterface; +use Magento\Ui\Component\Form\Fieldset; use Magento\Ui\Component\Layout\Tabs\TabInterface; /** @@ -89,6 +90,9 @@ protected function addChildren(array &$topNode, UiComponentInterface $component, $this->addWrappedBlock($childComponent, $childrenAreas); continue; } + if ($childComponent instanceof Fieldset && false === $childComponent->canShow()) { + continue; + } $name = $childComponent->getName(); $config = $childComponent->getData('config'); diff --git a/app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php b/app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php new file mode 100644 index 0000000000000..9a8cf28ae0719 --- /dev/null +++ b/app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php @@ -0,0 +1,69 @@ +context = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\ContextInterface::class) + ->getMockForAbstractClass(); + + $this->fieldset = new Fieldset( + $this->context, + [], + [] + ); + } + + /** + * Run test for getComponentName() method + * + * @return void + * + */ + public function testGetComponentName() + { + $this->assertEquals(self::NAME, $this->fieldset->getComponentName()); + } + + /** + * Run test for canShow() method + * + * @return void + * + */ + public function testCanShow() + { + $this->assertEquals(true, $this->fieldset->canShow()); + } +} diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/collection.js b/app/code/Magento/Ui/view/base/web/js/form/components/collection.js index 2c12486ceb519..dbea61b13e626 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/collection.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/collection.js @@ -5,6 +5,9 @@ /** * @api + * @deprecated as customer addresses are handled by ui components. + * This collection component manages rendering address list in Addresses tab of customer. + * Now address list is rendered with ui component listing. */ define([ 'underscore', @@ -46,6 +49,7 @@ define([ * @param {Object} elem - Incoming child. */ initElement: function (elem) { + debugger; this._super(); elem.activate(); @@ -153,6 +157,7 @@ define([ * Creates function that removes element * from collection using '_removeChild' method. * @param {Object} elem - Element that should be removed. + * @deprecated Not used anymore */ removeAddress: function (elem) { var self = this; @@ -169,7 +174,7 @@ define([ }, /** - * Removes elememt from both collection and data storage, + * Removes element from both collection and data storage, * activates first element if removed one was active, * triggers 'update' event. * diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js b/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js index c2a65371471c5..ae7f375bdca02 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js @@ -5,6 +5,9 @@ /** * @api + * @deprecated as customer addresses are handled by ui components. + * This item component renders address list item preview in Addresses tab. + * But now address list item is rendered with ui component in customer form. */ define([ 'underscore', @@ -19,7 +22,7 @@ define([ }; /** - * Parses incoming data and returnes result merged with default preview config + * Parses incoming data and returns result merged with default preview config * * @param {Object|String} data * @return {Object} diff --git a/app/code/Magento/Ui/view/base/web/templates/form/insert.html b/app/code/Magento/Ui/view/base/web/templates/form/insert.html index e19b2784e6bc6..e590b5e2adedf 100644 --- a/app/code/Magento/Ui/view/base/web/templates/form/insert.html +++ b/app/code/Magento/Ui/view/base/web/templates/form/insert.html @@ -6,9 +6,6 @@ -->
diff --git a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less index a80cc9a5163f8..8a5fe698eb196 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less @@ -3,14 +3,72 @@ // * See COPYING.txt for license details. // */ -// General rule hides group legend and shows first field label instead -// in app/design/adminhtml/Magento/backend/web/css/source/forms/_fields.less -// This must be reset for Customer Address page -.address-item-edit-content { +.customer_form_areas_address_address_customer_address_update_modal_update_customer_address_form_loader { .admin__field { - legend { - &.admin__field-label { - opacity: 1; + .admin__field { + .admin__field-label { + background: none; + } + } + } +} + +.customer-address-form { + + *, *:before, *:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + + address { + font-style: normal; + } + + .customer-default-billing-address-content, + .customer-default-shipping-address-content + { + float: left; + width: 550px; + } + + .edit-default-billing-address-button, + .edit-default-shipping-address-button { + float: left; + position: relative; + top: 2px; + } + + .edit-default-billing-address-button { + left: -336px; + } + + .edit-default-shipping-address-button { + left: -315px; + } + + .customer_form_areas_address_address_customer_address_listing { + clear: both; + } + + .add-new-address-button { + position: relative; + clear: both; + float: right; + margin-bottom: 30px; + } + + .address-information { + float: left; + margin-bottom: 20px; + + address { + float: left; + + .address_caption { + font-size: 18px; + font-weight: bold; + margin-bottom: 16px; } } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php new file mode 100644 index 0000000000000..6a41dd70e89e2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php @@ -0,0 +1,222 @@ +customerRepository = Bootstrap::getObjectManager()->get( + \Magento\Customer\Api\CustomerRepositoryInterface::class + ); + $this->accountManagement = Bootstrap::getObjectManager()->get( + \Magento\Customer\Api\AccountManagementInterface::class + ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->customerAddress = $this->objectManager->get(\Magento\Customer\Controller\Adminhtml\Address\Save::class); + } + + /** + * @inheritDoc + */ + protected function tearDown() + { + /** + * Unset customer data + */ + Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->setCustomerData(null); + + /** + * Unset messages + */ + Bootstrap::getObjectManager()->get(\Magento\Backend\Model\Session::class)->getMessages(true); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * + * Check that customer id set and addresses saved + */ + public function testSaveActionWithValidAddressData() + { + $customer = $this->customerRepository->get('customer5@example.com'); + $customerId = $customer->getId(); + $post = [ + 'parent_id' => $customerId, + 'firstname' => 'test firstname', + 'lastname' => 'test lastname', + 'street' => ['test street'], + 'city' => 'test city', + 'region_id' => 10, + 'country_id' => 'US', + 'postcode' => '01001', + 'telephone' => '+7000000001', + ]; + $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); + + $this->objectManager->get(\Magento\Backend\Model\Session::class)->setCustomerFormData($post); + + $this->customerAddress->execute(); + /** + * Check that errors was generated and set to session + */ + $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); + + /** + * Check that customer data were cleaned after it was saved successfully + */ + $this->assertEmpty($this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerData()); + + /** + * Check that success message is set + */ + $this->assertSessionMessages( + $this->logicalNot($this->isEmpty()), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + + $customer = $this->customerRepository->getById($customerId); + + $this->assertEquals('Firstname', $customer->getFirstname()); + $addresses = $customer->getAddresses(); + $this->assertCount(1, $addresses); + $this->assertNull($this->accountManagement->getDefaultBillingAddress($customerId)); + $this->assertNull($this->accountManagement->getDefaultShippingAddress($customerId)); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * + * Check that customer id set and addresses saved + */ + public function testSaveActionWithDefaultShippingAndBilling() + { + $customer = $this->customerRepository->get('customer5@example.com'); + $customerId = $customer->getId(); + $post = [ + 'parent_id' => $customerId, + 'firstname' => 'test firstname', + 'lastname' => 'test lastname', + 'street' => ['test street'], + 'city' => 'test city', + 'region_id' => 10, + 'country_id' => 'US', + 'postcode' => '01001', + 'telephone' => '+7000000001', + 'default_billing' => true, + 'default_shipping' => true + ]; + $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); + + $this->objectManager->get(\Magento\Backend\Model\Session::class)->setCustomerFormData($post); + + $this->customerAddress->execute(); + /** + * Check that errors was generated and set to session + */ + $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); + + /** + * Check that customer data were cleaned after it was saved successfully + */ + $this->assertEmpty($this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerData()); + + /** + * Check that success message is set + */ + $this->assertSessionMessages( + $this->logicalNot($this->isEmpty()), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + + /** + * Remove stored customer from registry + */ + $this->objectManager->get(\Magento\Customer\Model\CustomerRegistry::class)->remove($customerId); + $customer = $this->customerRepository->get('customer5@example.com'); + $this->assertEquals('Firstname', $customer->getFirstname()); + $addresses = $customer->getAddresses(); + $this->assertCount(1, $addresses); + + $this->assertNotNull($this->accountManagement->getDefaultBillingAddress($customerId)); + $this->assertNotNull($this->accountManagement->getDefaultShippingAddress($customerId)); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer_sample.php + * + * Check that customer id set and addresses saved + */ + public function testSaveActionWithExistingAdresses() + { + $customer = $this->customerRepository->get('customer@example.com'); + $customerId = $customer->getId(); + $post = [ + 'parent_id' => $customerId, + 'firstname' => 'test firstname', + 'lastname' => 'test lastname', + 'street' => ['test street'], + 'city' => 'test city', + 'region_id' => 10, + 'country_id' => 'US', + 'postcode' => '01001', + 'telephone' => '+7000000001', + ]; + $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); + + $this->objectManager->get(\Magento\Backend\Model\Session::class)->setCustomerFormData($post); + + $this->customerAddress->execute(); + /** + * Check that errors was generated and set to session + */ + $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); + + /** + * Check that customer data were cleaned after it was saved successfully + */ + $this->assertEmpty($this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerData()); + + /** + * Check that success message is set + */ + $this->assertSessionMessages( + $this->logicalNot($this->isEmpty()), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + + $customer = $this->customerRepository->getById($customerId); + + $this->assertEquals('test firstname', $customer->getFirstname()); + $addresses = $customer->getAddresses(); + $this->assertCount(4, $addresses); + } +} diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt index 877583e5b6a29..5c5c380c4dd33 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt @@ -1 +1 @@ -# Format: or simply +# Format: or simply \ No newline at end of file diff --git a/setup/performance-toolkit/config/customerConfig.xml b/setup/performance-toolkit/config/customerConfig.xml index 8fd74d7b53885..b77d9f6d4c941 100644 --- a/setup/performance-toolkit/config/customerConfig.xml +++ b/setup/performance-toolkit/config/customerConfig.xml @@ -6,5 +6,5 @@ */ --> - 2 + 5 From e2b3f791b49e961f757685eaa06b3f221b718148 Mon Sep 17 00:00:00 2001 From: Ruslan Kostiv Date: Fri, 26 Oct 2018 16:35:24 +0300 Subject: [PATCH 064/704] MAGETWO-95671: Remove address id from dialog alerts --- .../Component/Listing/Address/Column/Actions.php | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php index 75c02974ded8f..e0fea85bd3d80 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php @@ -89,10 +89,7 @@ public function prepareDataSource(array $dataSource): array 'label' => __('Set as default shipping'), 'confirm' => [ 'title' => __('Set address as default shipping'), - 'message' => __( - 'Are you sure you want to set the address with ID: %1 as default shipping address?', - $item['entity_id'] - ) + 'message' => __('Are you sure you want to set the address as default shipping address?') ] ]; @@ -104,10 +101,7 @@ public function prepareDataSource(array $dataSource): array 'label' => __('Set as default billing'), 'confirm' => [ 'title' => __('Set address as default billing'), - 'message' => __( - 'Are you sure you want to set the address with ID: %1 as default billing address?', - $item['entity_id'] - ) + 'message' => __('Are you sure you want to set the address as default billing address?') ] ]; @@ -119,10 +113,7 @@ public function prepareDataSource(array $dataSource): array 'label' => __('Delete'), 'confirm' => [ 'title' => __('Delete address'), - 'message' => __( - 'Are you sure you want to delete the address with ID: %1?', - $item['entity_id'] - ) + 'message' => __('Are you sure you want to delete the address?') ] ]; } From f1a23e4418aeb12f0011af06dc40e2b3b1f17539 Mon Sep 17 00:00:00 2001 From: Ruslan Kostiv Date: Fri, 26 Oct 2018 16:40:35 +0300 Subject: [PATCH 065/704] MAGETWO-95671: Remove address id from dialog alerts --- .../Listing/Address/Column/Actions.php | 20 +++++++++---------- .../ui_component/customer_address_form.xml | 12 +++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php index e0fea85bd3d80..e44dc7988761f 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php @@ -81,27 +81,27 @@ public function prepareDataSource(array $dataSource): array 'hidden' => false, ]; - $item[$name]['set_default_shipping'] = [ + $item[$name]['set_default_billing'] = [ 'href' => $this->urlBuilder->getUrl( - self::CUSTOMER_ADDRESS_PATH_DEFAULT_SHIPPING, + self::CUSTOMER_ADDRESS_PATH_DEFAULT_BILLING, ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] ), - 'label' => __('Set as default shipping'), + 'label' => __('Set as default billing'), 'confirm' => [ - 'title' => __('Set address as default shipping'), - 'message' => __('Are you sure you want to set the address as default shipping address?') + 'title' => __('Set address as default billing'), + 'message' => __('Are you sure you want to set the address as default billing address?') ] ]; - $item[$name]['set_default_billing'] = [ + $item[$name]['set_default_shipping'] = [ 'href' => $this->urlBuilder->getUrl( - self::CUSTOMER_ADDRESS_PATH_DEFAULT_BILLING, + self::CUSTOMER_ADDRESS_PATH_DEFAULT_SHIPPING, ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] ), - 'label' => __('Set as default billing'), + 'label' => __('Set as default shipping'), 'confirm' => [ - 'title' => __('Set address as default billing'), - 'message' => __('Are you sure you want to set the address as default billing address?') + 'title' => __('Set address as default shipping'), + 'message' => __('Are you sure you want to set the address as default shipping address?') ] ]; diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index a628c1b1b0b1e..9e432f9f10e0c 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -60,7 +60,7 @@ text - + 0 @@ -68,8 +68,8 @@ boolean - - default_shipping + + default_billing @@ -83,7 +83,7 @@ - + 0 @@ -91,8 +91,8 @@ boolean - - default_billing + + default_shipping From 0288d57b0f040bef1966b7358c97d0ea5a6a324a Mon Sep 17 00:00:00 2001 From: Yauhen_Lyskavets Date: Fri, 26 Oct 2018 16:47:19 +0300 Subject: [PATCH 066/704] MAGETWO-95813: Only two bundle options are added to the cart - Bug fix. --- app/code/Magento/Bundle/Model/Product/Type.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Model/Product/Type.php b/app/code/Magento/Bundle/Model/Product/Type.php index 92bada8094c7e..2eec13d291390 100644 --- a/app/code/Magento/Bundle/Model/Product/Type.php +++ b/app/code/Magento/Bundle/Model/Product/Type.php @@ -822,7 +822,7 @@ private function multiToFlatArray(array $array) $flatArray = []; foreach ($array as $key => $value) { if (is_array($value)) { - $flatArray = array_merge($flatArray, $this->multiToFlatArray($value)); + $flatArray = $flatArray + $this->multiToFlatArray($value); } else { $flatArray[$key] = $value; } From d51c135b4b27c69e36bf45f6caf7ba92c0c67131 Mon Sep 17 00:00:00 2001 From: Ruslan Kostiv Date: Fri, 26 Oct 2018 18:23:15 +0300 Subject: [PATCH 067/704] MAGETWO-95687: Remove old implementation of customer addresses tab --- .../Adminhtml/Edit/Address/CancelButton.php | 2 +- .../Adminhtml/Edit/Address/SaveButton.php | 2 +- .../Controller/Adminhtml/Address/Save.php | 3 +- .../Controller/Adminhtml/Address/Validate.php | 4 +-- .../Controller/Adminhtml/Index/Save.php | 2 ++ .../Controller/Adminhtml/Index/Validate.php | 3 ++ .../Customer/Model/Address/DataProvider.php | 31 +++++-------------- .../Customer/Model/Customer/DataProvider.php | 1 + .../DataProviderWithDefaultAddresses.php | 20 +----------- .../Customer/Model/Metadata/Form/File.php | 17 +++++----- .../ResourceModel/Address/Grid/Collection.php | 4 +-- .../Test/Unit/Controller/Address/SaveTest.php | 2 -- .../Unit/Model/Customer/DataProviderTest.php | 1 + .../Magento/Ui/Component/Form/Fieldset.php | 2 ++ 14 files changed, 34 insertions(+), 60 deletions(-) diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php index 80d9780f819d0..6270e8073d0e8 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php @@ -14,7 +14,7 @@ class CancelButton extends GenericButton implements ButtonProviderInterface { /** - * {@inheritdoc} + * @inheritdoc * * @return array */ diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php index 706ef32c9e5a5..0d3a9f57ff5a3 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/SaveButton.php @@ -14,7 +14,7 @@ class SaveButton extends GenericButton implements ButtonProviderInterface { /** - * {@inheritdoc} + * @inheritdoc * * @return array */ diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php index df041ac4e1202..e1d605a8d0890 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php @@ -135,7 +135,8 @@ public function execute(): Redirect $this->logger->critical($e); } catch (\Exception $e) { $this->messageManager->addExceptionMessage( - $e, __('We can\'t change customer address right now.') + $e, + __('We can\'t change customer address right now.') ); } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php index 01ce720a20e63..696bf3099db11 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php @@ -33,9 +33,9 @@ class Validate extends \Magento\Backend\App\Action implements HttpPostActionInte private $formFactory; /** - * @param Action\Context $context + * @param Action\Context $context * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory - * @param \Magento\Customer\Model\Metadata\FormFactory $formFactory + * @param \Magento\Customer\Model\Metadata\FormFactory $formFactory */ public function __construct( Action\Context $context, diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index aed7908337ee1..d80c712914ef0 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -15,6 +15,8 @@ use Magento\Framework\Exception\LocalizedException; /** + * Save customer action. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Save extends \Magento\Customer\Controller\Adminhtml\Index implements HttpPostActionInterface diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php index 67adf98d6c718..d91bc7424bffe 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Validate.php @@ -11,6 +11,9 @@ use Magento\Framework\Message\Error; use Magento\Customer\Controller\Adminhtml\Index as CustomerAction; +/** + * Class for validation of customer + */ class Validate extends CustomerAction implements HttpPostActionInterface, HttpGetActionInterface { /** diff --git a/app/code/Magento/Customer/Model/Address/DataProvider.php b/app/code/Magento/Customer/Model/Address/DataProvider.php index 34f4b8b4eca89..c92cd731db743 100644 --- a/app/code/Magento/Customer/Model/Address/DataProvider.php +++ b/app/code/Magento/Customer/Model/Address/DataProvider.php @@ -27,9 +27,9 @@ use Magento\Customer\Model\FileProcessorFactory; /** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * * Dataprovider for customer address grid. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider { @@ -53,11 +53,6 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider */ private $loadedData; - /** - * @var Config - */ - private $eavConfig; - /** * EAV attribute properties to fetch from meta storage * @var array @@ -154,6 +149,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider * @param ContextInterface $context * @param FileProcessorFactory $fileProcessorFactory * @param \Magento\Customer\Model\Config\Share $shareConfig + * @param CountryWithWebsites $countryWithWebsites * @param array $meta * @param array $data * @param bool $allowToShowHiddenAttributes @@ -170,6 +166,7 @@ public function __construct( ContextInterface $context, FileProcessorFactory $fileProcessorFactory, \Magento\Customer\Model\Config\Share $shareConfig, + CountryWithWebsites $countryWithWebsites, array $meta = [], array $data = [], $allowToShowHiddenAttributes = true @@ -182,6 +179,7 @@ public function __construct( $this->allowToShowHiddenAttributes = $allowToShowHiddenAttributes; $this->context = $context; $this->fileProcessorFactory = $fileProcessorFactory; + $this->countryWithWebsiteSource = $countryWithWebsites; $this->shareConfig = $shareConfig; $this->meta['general']['children'] = $this->getAttributesMeta( $eavConfig->getEntityType('customer_address') @@ -323,7 +321,7 @@ protected function getAttributesMeta(Type $entityType): array if ($attribute->usesSource()) { if ($code == AddressInterface::COUNTRY_ID) { - $meta[$code]['arguments']['data']['config']['options'] = $this->getCountryWithWebsiteSource() + $meta[$code]['arguments']['data']['config']['options'] = $this->countryWithWebsiteSource ->getAllOptions(); } else { $meta[$code]['arguments']['data']['config']['options'] = $attribute->getSource()->getAllOptions(); @@ -364,25 +362,10 @@ private function processFrontendInput(AttributeInterface $attribute, array &$met } } - /** - * Retrieve Country With Websites Source - * - * @return CountryWithWebsites - * @deprecated 100.2.0 - */ - private function getCountryWithWebsiteSource(): CountryWithWebsites - { - if (!$this->countryWithWebsiteSource) { - $this->countryWithWebsiteSource = ObjectManager::getInstance()->get(CountryWithWebsites::class); - } - - return $this->countryWithWebsiteSource; - } - /** * Detect can we show attribute on specific form or not * - * @param Attribute $customerAttribute + * @param AbstractAttribute $customerAttribute * @return bool */ private function canShowAttribute(AbstractAttribute $customerAttribute): bool diff --git a/app/code/Magento/Customer/Model/Customer/DataProvider.php b/app/code/Magento/Customer/Model/Customer/DataProvider.php index ce976d3f62c74..c834dd26824cd 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProvider.php +++ b/app/code/Magento/Customer/Model/Customer/DataProvider.php @@ -31,6 +31,7 @@ /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * + * @deprecated \Magento\Customer\Model\Address\DataProvider is used instead * @api * @since 100.0.2 */ diff --git a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php index d52c94fb034c6..2e47787b9adbc 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php +++ b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php @@ -186,9 +186,6 @@ public function __construct( $this->meta['customer']['children'] = $this->getAttributesMeta( $eavConfig->getEntityType('customer') ); -// $this->meta['address']['children'] = $this->getAttributesMeta( -// $eavConfig->getEntityType('customer_address') -// ); } /** @@ -300,7 +297,7 @@ private function getFileUploaderData( $file = $customerData[$attributeCode] ?? ''; /** @var FileProcessor $fileProcessor */ - $fileProcessor = $this->getFileProcessorFactory()->create([ + $fileProcessor = $this->fileProcessorFactory->create([ 'entityTypeCode' => $entityType->getEntityTypeCode(), ]); @@ -567,19 +564,4 @@ protected function prepareAddressData($addressId, array &$addresses, array $cust $addresses[$addressId]['street'] = explode("\n", $addresses[$addressId]['street']); } } - - /** - * Get FileProcessorFactory instance - * - * @return FileProcessorFactory - * @deprecated 100.1.3 - */ - private function getFileProcessorFactory(): FileProcessorFactory - { - if ($this->fileProcessorFactory === null) { - $this->fileProcessorFactory = ObjectManager::getInstance() - ->get(\Magento\Customer\Model\FileProcessorFactory::class); - } - return $this->fileProcessorFactory; - } } diff --git a/app/code/Magento/Customer/Model/Metadata/Form/File.php b/app/code/Magento/Customer/Model/Metadata/Form/File.php index aca5b277186ca..b9bec01f9ba7c 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/File.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/File.php @@ -15,6 +15,8 @@ use Magento\Framework\Filesystem; /** + * Processes files that are save for customer. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class File extends AbstractData @@ -66,7 +68,7 @@ class File extends AbstractData * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute * @param \Magento\Framework\Locale\ResolverInterface $localeResolver - * @param null $value + * @param array|string $value * @param string $entityTypeCode * @param bool $isAjax * @param \Magento\Framework\Url\EncoderInterface $urlEncoder @@ -101,7 +103,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function extractValue(\Magento\Framework\App\RequestInterface $request) @@ -160,8 +162,7 @@ public function extractValue(\Magento\Framework\App\RequestInterface $request) } /** - * Validate file by attribute validate rules - * Return array of errors + * Validate file by attribute validate rules. Returns array of errors. * * @param array $value * @return string[] @@ -232,7 +233,7 @@ protected function _isUploadedFile($filename) } /** - * {@inheritdoc} + * @inheritdoc * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -273,7 +274,7 @@ public function validateValue($value) } /** - * {@inheritdoc} + * @inheritdoc * * @return ImageContentInterface|array|string|null */ @@ -358,7 +359,7 @@ protected function processInputFieldValue($value) } /** - * {@inheritdoc} + * @inheritdoc */ public function restoreValue($value) { @@ -366,7 +367,7 @@ public function restoreValue($value) } /** - * {@inheritdoc} + * @inheritdoc */ public function outputValue($format = \Magento\Customer\Model\Metadata\ElementFactory::OUTPUT_FORMAT_TEXT) { diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php index 83129fee9b59b..8026349563867 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php @@ -92,7 +92,7 @@ protected function _initSelect() } /** - * {@inheritdoc} + * @inheritdoc * * @return AggregationInterface */ @@ -102,7 +102,7 @@ public function getAggregations() } /** - * {@inheritdoc} + * @inheritdoc * * @param AggregationInterface $aggregations * @return $this diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php index 47088eece23ed..863ce260ab68c 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/SaveTest.php @@ -24,7 +24,6 @@ class SaveTest extends \PHPUnit\Framework\TestCase */ private $addressRepositoryMock; - /** * @var \Magento\Customer\Model\Metadata\FormFactory|\PHPUnit_Framework_MockObject_MockObject */ @@ -65,7 +64,6 @@ class SaveTest extends \PHPUnit\Framework\TestCase */ private $messageManagerMock; - /** * @inheritdoc */ diff --git a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php index 50c21379054bf..bf6d3f0f9bbc5 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php @@ -21,6 +21,7 @@ * * Test for class \Magento\Customer\Model\Customer\DataProvider * + * @deprecated tested class is not used. * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DataProviderTest extends \PHPUnit\Framework\TestCase diff --git a/app/code/Magento/Ui/Component/Form/Fieldset.php b/app/code/Magento/Ui/Component/Form/Fieldset.php index ebfe58f3abb89..ef115fe459eba 100644 --- a/app/code/Magento/Ui/Component/Form/Fieldset.php +++ b/app/code/Magento/Ui/Component/Form/Fieldset.php @@ -8,6 +8,8 @@ use Magento\Ui\Component\AbstractComponent; /** + * Fieldset UI Component. + * * @api * @since 100.0.2 */ From dc506771ce4353b0d3a272368acd7322dbf391c8 Mon Sep 17 00:00:00 2001 From: Ruslan Kostiv Date: Fri, 26 Oct 2018 19:16:12 +0300 Subject: [PATCH 068/704] MAGETWO-95687: Remove old implementation of customer addresses tab --- .../Customer/Model/Address/DataProvider.php | 2 +- .../DataProviderWithDefaultAddresses.php | 25 ------------------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/app/code/Magento/Customer/Model/Address/DataProvider.php b/app/code/Magento/Customer/Model/Address/DataProvider.php index c92cd731db743..7f23deddd89a6 100644 --- a/app/code/Magento/Customer/Model/Address/DataProvider.php +++ b/app/code/Magento/Customer/Model/Address/DataProvider.php @@ -384,7 +384,7 @@ private function canShowAttribute(AbstractAttribute $customerAttribute): bool /** * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... * - * @param Attribute $customerAttribute + * @param AbstractAttribute $customerAttribute * @return bool */ private function canShowAttributeInForm(AbstractAttribute $customerAttribute): bool diff --git a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php index 2e47787b9adbc..7ba6484dc3f2b 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php +++ b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php @@ -539,29 +539,4 @@ private function processFrontendInput(AttributeInterface $attribute, array &$met ]; } } - - /** - * Prepare address data - * - * @param int $addressId - * @param array $addresses - * @param array $customer - * @return void - */ - protected function prepareAddressData($addressId, array &$addresses, array $customer) - { - if (isset($customer['default_billing']) - && $addressId == $customer['default_billing'] - ) { - $addresses[$addressId]['default_billing'] = $customer['default_billing']; - } - if (isset($customer['default_shipping']) - && $addressId == $customer['default_shipping'] - ) { - $addresses[$addressId]['default_shipping'] = $customer['default_shipping']; - } - if (isset($addresses[$addressId]['street']) && !\is_array($addresses[$addressId]['street'])) { - $addresses[$addressId]['street'] = explode("\n", $addresses[$addressId]['street']); - } - } } From 69f367a983562431aad32f37adc817556d5907e4 Mon Sep 17 00:00:00 2001 From: Oleksii Korshenko Date: Fri, 26 Oct 2018 12:56:58 -0500 Subject: [PATCH 069/704] Added allure report to the unit tests - blacklisted incorrect annotations --- dev/tests/unit/phpunit.xml.dist | 48 +++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/dev/tests/unit/phpunit.xml.dist b/dev/tests/unit/phpunit.xml.dist index ad8406ee92c93..f887581acd001 100644 --- a/dev/tests/unit/phpunit.xml.dist +++ b/dev/tests/unit/phpunit.xml.dist @@ -44,6 +44,54 @@ var/allure-results true + + + codingStandardsIgnoreStart + + + codingStandardsIgnoreEnd + + + cover + + + expectedExceptionMessageRegExp + + + + expectedExceptionText + + + expectedMessage + + + message + + + exceptedExceptionMessage + + + exectedExceptionMessage + + + ExceptedExceptionMessage + + + expected + + + expecteExceptionMessage + + + true + + + assertException + + + paran + + From b2444cb131964ed8d722c4b0191b84bc2cba2c08 Mon Sep 17 00:00:00 2001 From: Oleksii Korshenko Date: Fri, 26 Oct 2018 14:58:35 -0500 Subject: [PATCH 070/704] Added allure report to the unit tests --- dev/tests/unit/phpunit.xml.dist | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dev/tests/unit/phpunit.xml.dist b/dev/tests/unit/phpunit.xml.dist index f887581acd001..3de27d9da88b8 100644 --- a/dev/tests/unit/phpunit.xml.dist +++ b/dev/tests/unit/phpunit.xml.dist @@ -88,9 +88,16 @@ assertException - + exceptionMessage paran + + + exceptionMessage + + + magentoAppArea + From 5854ca32e8536be9cb0ffb9dce44c0c38ecde6e0 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova Date: Mon, 29 Oct 2018 10:10:02 +0300 Subject: [PATCH 071/704] MAGETWO-95809: Item row total display incorrect value in API response - Changing row total in webapi response --- .../Sales/Plugin/DataObjectProcessor.php | 55 ++++++++++++++ app/code/Magento/Sales/etc/webapi_rest/di.xml | 3 + app/code/Magento/Sales/etc/webapi_soap/di.xml | 3 + .../Sales/Service/V1/OrderItemGetTest.php | 30 ++++++++ .../Sales/_files/order_with_discount.php | 71 +++++++++++++++++++ .../_files/order_with_discount_rollback.php | 8 +++ 6 files changed, 170 insertions(+) create mode 100644 app/code/Magento/Sales/Plugin/DataObjectProcessor.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount_rollback.php diff --git a/app/code/Magento/Sales/Plugin/DataObjectProcessor.php b/app/code/Magento/Sales/Plugin/DataObjectProcessor.php new file mode 100644 index 0000000000000..5c61cdda9efc2 --- /dev/null +++ b/app/code/Magento/Sales/Plugin/DataObjectProcessor.php @@ -0,0 +1,55 @@ +priceRenderer = $priceRenderer; + } + + /** + * Changing row total for webapi order item response. + * + * @param Subject $subject + * @param array $result + * @param mixed $dataObject + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterBuildOutputDataArray( + Subject $subject, + $result, + $dataObject + ) { + if ($dataObject instanceof OrderItem) { + $result[OrderItemInterface::ROW_TOTAL] = $this->priceRenderer->getTotalAmount($dataObject); + $result[OrderItemInterface::BASE_ROW_TOTAL] = $this->priceRenderer->getBaseTotalAmount($dataObject); + } + + return $result; + } +} diff --git a/app/code/Magento/Sales/etc/webapi_rest/di.xml b/app/code/Magento/Sales/etc/webapi_rest/di.xml index 47fb3f188513c..70fe957673517 100644 --- a/app/code/Magento/Sales/etc/webapi_rest/di.xml +++ b/app/code/Magento/Sales/etc/webapi_rest/di.xml @@ -15,4 +15,7 @@ + + + diff --git a/app/code/Magento/Sales/etc/webapi_soap/di.xml b/app/code/Magento/Sales/etc/webapi_soap/di.xml index 47fb3f188513c..70fe957673517 100644 --- a/app/code/Magento/Sales/etc/webapi_soap/di.xml +++ b/app/code/Magento/Sales/etc/webapi_soap/di.xml @@ -15,4 +15,7 @@ + + + diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderItemGetTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderItemGetTest.php index 3ab93f9aecb99..592bdf3d584a9 100644 --- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderItemGetTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderItemGetTest.php @@ -77,4 +77,34 @@ protected function assertOrderItem(\Magento\Sales\Model\Order\Item $orderItem, a $this->assertEquals($orderItem->getBasePrice(), $response['base_price']); $this->assertEquals($orderItem->getRowTotal(), $response['row_total']); } + + /** + * @magentoApiDataFixture Magento/Sales/_files/order_with_discount.php + */ + public function testGetOrderWithDiscount() + { + /** @var \Magento\Sales\Model\Order $order */ + $order = $this->objectManager->create(\Magento\Sales\Model\Order::class); + $order->loadByIncrementId(self::ORDER_INCREMENT_ID); + /** @var \Magento\Sales\Model\Order\Item $orderItem */ + $orderItem = current($order->getItems()); + + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . '/' . $orderItem->getId(), + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'get', + ], + ]; + + $response = $this->_webApiCall($serviceInfo, ['id' => $orderItem->getId()]); + + $this->assertTrue(is_array($response)); + $this->assertEquals(8.00, $response['row_total']); + $this->assertEquals(8.00, $response['base_row_total']); + } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount.php new file mode 100644 index 0000000000000..a83b01589ea9c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount.php @@ -0,0 +1,71 @@ +create(OrderAddress::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); + +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +/** @var Payment $payment */ +$payment = $objectManager->create(Payment::class); +$payment->setMethod('checkmo') + ->setAdditionalInformation('last_trans_id', '11122') + ->setAdditionalInformation( + 'metadata', + [ + 'type' => 'free', + 'fraudulent' => false, + ] + ); + +/** @var OrderItem $orderItem */ +$orderItem = $objectManager->create(OrderItem::class); +$orderItem->setProductId($product->getId()) + ->setQtyOrdered(2) + ->setBasePrice($product->getPrice()) + ->setPrice($product->getPrice()) + ->setRowTotal($product->getPrice()) + ->setProductType('simple') + ->setDiscountAmount(2) + ->setBaseRowTotal($product->getPrice()) + ->setBaseDiscountAmount(2); + +/** @var Order $order */ +$order = $objectManager->create(Order::class); +$order->setIncrementId('100000001') + ->setState(Order::STATE_PROCESSING) + ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)) + ->setSubtotal(100) + ->setGrandTotal(100) + ->setBaseSubtotal(100) + ->setBaseGrandTotal(100) + ->setCustomerIsGuest(true) + ->setCustomerEmail('customer@null.com') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setStoreId($objectManager->get(StoreManagerInterface::class)->getStore()->getId()) + ->addItem($orderItem) + ->setPayment($payment); + +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount_rollback.php new file mode 100644 index 0000000000000..1fb4b4636ab29 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_discount_rollback.php @@ -0,0 +1,8 @@ + Date: Mon, 29 Oct 2018 11:44:03 +0400 Subject: [PATCH 072/704] MAGETWO-95830: Cannot create credit memo if the used in the order cart rule is deleted - Add automated test --- ...reateCreditMemoWhenCartRuleDeletedTest.xml | 109 ++++++++++++++++++ .../AdminCreateCartPriceRuleActionGroup.xml | 16 ++- 2 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Sales/Test/Mftf/Test/CreateCreditMemoWhenCartRuleDeletedTest.xml diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateCreditMemoWhenCartRuleDeletedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateCreditMemoWhenCartRuleDeletedTest.xml new file mode 100644 index 0000000000000..6ca7c838b7fe7 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateCreditMemoWhenCartRuleDeletedTest.xml @@ -0,0 +1,109 @@ + + + + + + + + + + <description value="Verify Credit Memo created if the used in the order cart rule is deleted"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-95894"/> + <group value="sales"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="product"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + + <!-- Create Cart Price Rule with a specific coupon --> + <actionGroup ref="AdminCreateCartPriceRuleWithCouponCode" stepKey="createCartPriceRule"> + <argument name="ruleName" value="TestSalesRule"/> + <argument name="couponCode" value="_defaultCoupon.code"/> + </actionGroup> + + <!--Go to Storefront. Add product to cart--> + <amOnPage url="/$$product.name$$.html" stepKey="GoToProduct"/> + <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="AddProductToCard"> + <argument name="productName" value="$$product.name$$"/> + </actionGroup> + <!--Proceed to checkout--> + <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> + <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection"> + <argument name="customerVar" value="CustomerEntityOne" /> + <argument name="customerAddressVar" value="CustomerAddressSimple" /> + </actionGroup> + + <click selector="{{DiscountSection.DiscountTab}}" stepKey="clickToAddDiscount"/> + <fillField selector="{{DiscountSection.DiscountInput}}" userInput="{{_defaultCoupon.code}}" stepKey="TypeDiscountCode"/> + <click selector="{{DiscountSection.ApplyCodeBtn}}" stepKey="clickToApplyDiscount"/> + <waitForPageLoad stepKey="WaitForDiscountToBeAdded"/> + <see userInput="Your coupon was successfully applied." stepKey="verifyText"/> + + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> + + <!--Proceed to Admin panel > SALES > Orders. Created order should be in Processing status--> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrdersIndexPage"/> + <waitForPageLoad stepKey="waitForOrderIndexPage"/> + + <!-- Open Order --> + <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <argument name="orderId" value="$grabOrderNumber"/> + </actionGroup> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> + <waitForPageLoad stepKey="waitForCreatedOrderPageOpened"/> + + <!--Click *Invoice* button--> + <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seeNewInvoiceInPageTitle" after="clickInvoiceButton"/> + <waitForPageLoad stepKey="waitForInvoicePageOpened"/> + <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> + <waitForPageLoad stepKey="waitForInvoiceSaved"/> + <see userInput="The invoice has been created." stepKey="seeCorrectMessage"/> + + <!-- Delete the cart price rule --> + <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCartPriceRule"> + <argument name="ruleName" value="{{TestSalesRule.name}}"/> + </actionGroup> + + <!--Proceed to Admin panel > SALES > Orders. Created order should be in Processing status--> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrdersIndexPage2"/> + <waitForPageLoad stepKey="waitForOrderIndexPage2"/> + + <!-- Open Order --> + <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById2"> + <argument name="orderId" value="$grabOrderNumber"/> + </actionGroup> + <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow2"/> + <waitForPageLoad stepKey="waitForCreatedOrderPageOpened2"/> + + <!--Admin create credit memo for order--> + <comment userInput="Admin creates credit memo" stepKey="createCreditMemoComment"/> + <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreditMemoAction"/> + <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Memo" stepKey="seeNewMemoInPageTitle"/> + <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickRefundOffline"/> + + <!--Make sure that Credit memo was created successfully--> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the credit memo." stepKey="seeCreditMemoSuccess"/> + + <after> + <deleteData createDataKey="product" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logOut"/> + </after> + </test> +</tests> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml index 87947fba8095a..a8dc13d85a360 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminCreateCartPriceRuleActionGroup.xml @@ -6,7 +6,7 @@ */ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminCreateCartPriceRuleActionGroup"> <arguments> <argument name="ruleName"/> @@ -23,4 +23,18 @@ <click selector="{{AdminCartPriceRulesFormSection.save}}" stepKey="clickSaveButton"/> <see selector="{{AdminCartPriceRulesFormSection.successMessage}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> </actionGroup> + + <actionGroup name="AdminCreateCartPriceRuleWithCouponCode" extends="AdminCreateCartPriceRuleActionGroup"> + <arguments> + <argument name="couponCode" defaultValue="_defaultCoupon.code"/> + </arguments> + <remove keyForRemoval="selectActionType"/> + <remove keyForRemoval="fillDiscountAmount"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.coupon}}" userInput="Specific Coupon" stepKey="selectCouponType" after="selectCustomerGroup"/> + <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.couponCode}}" stepKey="waitForElementVisible" after="selectCouponType"/> + <fillField selector="{{AdminCartPriceRulesFormSection.couponCode}}" userInput="{{couponCode}}" stepKey="fillCouponCode" after="waitForElementVisible"/> + <fillField selector="{{AdminCartPriceRulesFormSection.userPerCoupon}}" userInput="99" stepKey="fillUserPerCoupon" after="fillCouponCode"/> + <selectOption selector="{{AdminCartPriceRulesFormSection.apply}}" userInput="Fixed amount discount for whole cart" stepKey="selectActionTypeToFixed" after="clickToExpandActions"/> + <fillField selector="{{AdminCartPriceRulesFormSection.discountAmount}}" userInput="1" stepKey="fillDiscountAmount" after="selectActionTypeToFixed"/> + </actionGroup> </actionGroups> From 1e3dcd554d5f4f6e00ac88aaecc858aeb295ef2a Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Mon, 29 Oct 2018 15:30:05 +0400 Subject: [PATCH 073/704] MAGETWO-95813: Only two bundle options are added to the cart - Add automated test. --- .../CreateBundleProductActionGroup.xml | 70 +++++++++ .../Mftf/Data/BundleProductsSummaryData.xml | 17 +++ .../Mftf/Section/StorefrontBundledSection.xml | 2 + .../StorefrontAddBundleOptionsToCartTest.xml | 140 ++++++++++++++++++ 4 files changed, 229 insertions(+) create mode 100644 app/code/Magento/Bundle/Test/Mftf/Data/BundleProductsSummaryData.xml create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CreateBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CreateBundleProductActionGroup.xml index 72e729111948f..d86d720ed7f5d 100644 --- a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CreateBundleProductActionGroup.xml +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/CreateBundleProductActionGroup.xml @@ -56,4 +56,74 @@ <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '0')}}" userInput="50" stepKey="fillQuantity1"/> <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '1')}}" userInput="50" stepKey="fillQuantity2"/> </actionGroup> + + <actionGroup name="addBundleOptionWithOneProduct" extends="addBundleOptionWithTwoProducts"> + <remove keyForRemoval="openProductFilters2"/> + <remove keyForRemoval="fillProductSkuFilter2"/> + <remove keyForRemoval="clickApplyFilters2"/> + <remove keyForRemoval="waitForFilteredGridLoad2"/> + <remove keyForRemoval="selectProduct2"/> + <remove keyForRemoval="selectProduct2"/> + <remove keyForRemoval="fillQuantity1"/> + <remove keyForRemoval="fillQuantity2"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '0')}}" userInput="1" stepKey="fillQuantity" after="clickAddButton1"/> + </actionGroup> + + <actionGroup name="addBundleOptionWithTreeProducts" extends="addBundleOptionWithTwoProducts"> + <arguments> + <argument name="prodTreeSku" type="string"/> + </arguments> + <remove keyForRemoval="fillQuantity1"/> + <remove keyForRemoval="fillQuantity2"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters3" after="selectProduct2"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters3" after="clickClearFilters3"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodTreeSku}}" stepKey="fillProductSkuFilter3" after="openProductFilters3"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters3" after="fillProductSkuFilter3"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad3" time="30" after="clickApplyFilters3"/> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct3" after="waitForFilteredGridLoad3"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '0')}}" userInput="1" stepKey="fillQuantity1" after="clickAddButton1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '1')}}" userInput="1" stepKey="fillQuantity2" after="fillQuantity1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '2')}}" userInput="1" stepKey="fillQuantity3" after="fillQuantity2"/> + </actionGroup> + + <actionGroup name="addBundleOptionWithSixProducts" extends="addBundleOptionWithTwoProducts"> + <arguments> + <argument name="prodTreeSku" type="string"/> + <argument name="prodFourSku" type="string"/> + <argument name="prodFiveSku" type="string"/> + <argument name="prodSixSku" type="string"/> + </arguments> + <remove keyForRemoval="fillQuantity1"/> + <remove keyForRemoval="fillQuantity2"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters3" after="selectProduct2"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters3" after="clickClearFilters3"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodTreeSku}}" stepKey="fillProductSkuFilter3" after="openProductFilters3"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters3" after="fillProductSkuFilter3"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad3" time="30" after="clickApplyFilters3"/> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct3" after="waitForFilteredGridLoad3"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters4" after="selectProduct3"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters4" after="clickClearFilters4"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodFourSku}}" stepKey="fillProductSkuFilter4" after="openProductFilters4"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters4" after="fillProductSkuFilter4"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad4" time="30" after="clickApplyFilters4"/> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct4" after="clickApplyFilters4"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters5" after="selectProduct4"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters5" after="clickClearFilters5"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodFiveSku}}" stepKey="fillProductSkuFilter5" after="openProductFilters5"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters5" after="fillProductSkuFilter5"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad5" time="30" after="clickApplyFilters5"/> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct5" after="waitForFilteredGridLoad5"/> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters6" after="selectProduct5"/> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters6" after="clickClearFilters6"/> + <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{prodSixSku}}" stepKey="fillProductSkuFilter6" after="openProductFilters6"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFilters6" after="fillProductSkuFilter6"/> + <waitForElementNotVisible selector="{{AdminProductGridSection.loadingMask}}" stepKey="waitForFilteredGridLoad6" time="30" after="clickApplyFilters6"/> + <checkOption selector="{{AdminAddProductsToOptionPanel.firstCheckbox}}" stepKey="selectProduct6" after="waitForFilteredGridLoad6"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '0')}}" userInput="2" stepKey="fillQuantity1" after="clickAddButton1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '1')}}" userInput="2" stepKey="fillQuantity2" after="fillQuantity1"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '2')}}" userInput="2" stepKey="fillQuantity3" after="fillQuantity2"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '3')}}" userInput="2" stepKey="fillQuantity4" after="fillQuantity3"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '4')}}" userInput="2" stepKey="fillQuantity5" after="fillQuantity4"/> + <fillField selector="{{AdminProductFormBundleSection.bundleOptionXProductYQuantity(x, '5')}}" userInput="2" stepKey="fillQuantity6" after="fillQuantity5"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/Data/BundleProductsSummaryData.xml b/app/code/Magento/Bundle/Test/Mftf/Data/BundleProductsSummaryData.xml new file mode 100644 index 0000000000000..5cd286c0c6aa1 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Data/BundleProductsSummaryData.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="BundleProductsSummary" type="Quote"> + <data key="subtotal">1,968.00</data> + <data key="shipping">5.00</data> + <data key="total">1,973.00</data> + <data key="shippingMethod">Flat Rate - Fixed</data> + </entity> +</entities> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml index 8d9f29814f762..05550f0cd1e3a 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml @@ -9,6 +9,8 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontBundledSection"> + <element name="productCheckbox" type="select" selector="//*[@id='customizeTitle']/following-sibling::div[{{arg1}}]//div[{{arg2}}][@class='field choice']/input" parameterized="true"/> + <element name="bundleProductsPrice" type="text" selector="//*[@class='bundle-info']//*[contains(@id,'product-price')]/span"/> <element name="nthBundledOption" type="input" selector=".option:nth-of-type({{numOption}}) .choice:nth-of-type({{numOptionSelect}}) input" parameterized="true"/> <element name="addToCart" type="button" selector="#bundle-slide" timeout="30"/> <element name="addToCartConfigured" type="button" selector="#product-addtocart-button" timeout="30"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml new file mode 100644 index 0000000000000..6ea5a2842470b --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml @@ -0,0 +1,140 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontAddBundleOptionsToCartTest"> + <annotations> + <features value="Bundle"/> + <stories value="MAGETWO-95813: Only two bundle options are added to the cart"/> + <title value="Checking adding of bundle options to the cart"/> + <description value="Verifying adding of bundle options to the cart"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-95933"/> + <group value="Bundle"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct2"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct3"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct4"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct5"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct6"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct7"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct8"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct9"/> + <createData entity="SimpleProduct2" stepKey="simpleProduct10"/> + </before> + <after> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="simpleProduct3" stepKey="deleteSimpleProduct3"/> + <deleteData createDataKey="simpleProduct4" stepKey="deleteSimpleProduct4"/> + <deleteData createDataKey="simpleProduct5" stepKey="deleteSimpleProduct5"/> + <deleteData createDataKey="simpleProduct6" stepKey="deleteSimpleProduct6"/> + <deleteData createDataKey="simpleProduct7" stepKey="deleteSimpleProduct7"/> + <deleteData createDataKey="simpleProduct8" stepKey="deleteSimpleProduct8"/> + <deleteData createDataKey="simpleProduct9" stepKey="deleteSimpleProduct9"/> + <deleteData createDataKey="simpleProduct10" stepKey="deleteSimpleProduct10"/> + <!--delete created bundle product--> + <actionGroup stepKey="deleteProduct1" ref="deleteProductBySku"> + <argument name="sku" value="{{BundleProduct.sku}}"/> + </actionGroup> + <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" + dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Start creating a bundle product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> + <waitForPageLoad stepKey="waitForProductList"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProduct"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + <actionGroup ref="fillProductNameAndSkuInProductForm" stepKey="fillNameAndSku"> + <argument name="product" value="BundleProduct"/> + </actionGroup> + + <!-- Add Option One, a "Checkbox" type option, with tree products --> + <actionGroup ref="addBundleOptionWithTreeProducts" stepKey="addBundleOptionWithTreeProducts"> + <argument name="x" value="0"/> + <argument name="n" value="1"/> + <argument name="prodOneSku" value="$$simpleProduct1.sku$$"/> + <argument name="prodTwoSku" value="$$simpleProduct2.sku$$"/> + <argument name="prodTreeSku" value="$$simpleProduct3.sku$$"/> + <argument name="optionTitle" value="Option One"/> + <argument name="inputType" value="checkbox"/> + </actionGroup> + + <!-- Add Option Two, a "Radio Buttons" type option, with one product --> + <actionGroup ref="addBundleOptionWithOneProduct" stepKey="addBundleOptionWithOneProduct"> + <argument name="x" value="1"/> + <argument name="n" value="2"/> + <argument name="prodOneSku" value="$$simpleProduct4.sku$$"/> + <argument name="prodTwoSku" value=""/> + <argument name="optionTitle" value="Option Two"/> + <argument name="inputType" value="radio"/> + </actionGroup> + + <!-- Add Option Tree, a "Checkbox" type option, with six products --> + <actionGroup ref="addBundleOptionWithSixProducts" stepKey="addBundleOptionWithSixProducts"> + <argument name="x" value="2"/> + <argument name="n" value="3"/> + <argument name="prodOneSku" value="$$simpleProduct5.sku$$"/> + <argument name="prodTwoSku" value="$$simpleProduct6.sku$$"/> + <argument name="prodTreeSku" value="$$simpleProduct7.sku$$"/> + <argument name="prodFourSku" value="$$simpleProduct8.sku$$"/> + <argument name="prodFiveSku" value="$$simpleProduct9.sku$$"/> + <argument name="prodSixSku" value="$$simpleProduct10.sku$$"/> + <argument name="optionTitle" value="Option Tree"/> + <argument name="inputType" value="checkbox"/> + </actionGroup> + + <!-- Save product--> + <actionGroup ref="saveProductForm" stepKey="saveProduct"/> + + <!--Go to Storefront and open Bundle Product page--> + <amOnPage url="{{BundleProduct.sku}}.html" stepKey="goToStorefront"/> + <waitForPageLoad stepKey="waitForStorefront"/> + + <!--Click "Customize and Add to Cart" button--> + <click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/> + + <!--Assert Bundle Product Price--> + <grabTextFrom selector="{{StorefrontBundledSection.bundleProductsPrice}}" stepKey="grabProductsPrice"/> + <assertEquals expected='$123.00' expectedType="string" actual="$grabProductsPrice" message="ExpectedPrice" stepKey="assertBundleProductPrice"/> + + <!--Chose all products from 1st & 3rd options --> + <click stepKey="selectProduct1" selector="{{StorefrontBundledSection.productCheckbox('1','1')}}"/> + <click stepKey="selectProduct2" selector="{{StorefrontBundledSection.productCheckbox('1','2')}}"/> + <click stepKey="selectProduct3" selector="{{StorefrontBundledSection.productCheckbox('1','3')}}"/> + <click stepKey="selectProduct5" selector="{{StorefrontBundledSection.productCheckbox('3','1')}}"/> + <click stepKey="selectProduct6" selector="{{StorefrontBundledSection.productCheckbox('3','2')}}"/> + <click stepKey="selectProduct7" selector="{{StorefrontBundledSection.productCheckbox('3','3')}}"/> + <click stepKey="selectProduct8" selector="{{StorefrontBundledSection.productCheckbox('3','4')}}"/> + <click stepKey="selectProduct9" selector="{{StorefrontBundledSection.productCheckbox('3','5')}}"/> + <click stepKey="selectProduct10" selector="{{StorefrontBundledSection.productCheckbox('3','6')}}"/> + + <!--Click "Add to Cart" button--> + <click selector="{{StorefrontBundleProductActionSection.addToCartButton}}" stepKey="clickAddBundleProductToCart"/> + <waitForPageLoad time="30" stepKey="waitForAddBundleProductPageLoad"/> + + <!--Click "mini cart" icon--> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCart"/> + <waitForPageLoad stepKey="waitForDetailsOpen"/> + + <!--Check all products and Cart Subtotal --> + <actionGroup ref="StorefrontCheckCartActionGroup" stepKey="cartAssert" after="waitForDetailsOpen"> + <argument name="subtotal" value="BundleProductsSummary.subtotal"/> + <argument name="shipping" value="BundleProductsSummary.shipping"/> + <argument name="shippingMethod" value="BundleProductsSummary.shippingMethod"/> + <argument name="total" value="BundleProductsSummary.total"/> + </actionGroup> + </test> +</tests> From 3d317714b5e5a6d190b1e930726e95501cf03631 Mon Sep 17 00:00:00 2001 From: bshevchenko <1408sheva@gmail.com> Date: Mon, 29 Oct 2018 16:31:09 +0200 Subject: [PATCH 074/704] MAGETWO-94840: Automate with MFTF Managing Advanced Prices from Shared Catalog Page --- .../Mftf/ActionGroup/AdminProductActionGroup.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 1f6c2ab4bb25f..b637ad1fc358c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -276,4 +276,18 @@ <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> + <!--Check tier price with a discount percentage on product--> + <actionGroup name="AssertDiscountsPercentageOfProducts"> + <arguments> + <argument name="amount" type="string" defaultValue="45"/> + </arguments> + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForCustomerGroupPriceAddButton"/> + <grabValueFrom selector="{{AdminProductFormAdvancedPricingSection.productTierPricePercentageValuePriceInput('0')}}" stepKey="grabProductTierPriceInput"/> + <assertEquals stepKey="assertProductTierPriceInput"> + <expectedResult type="string">{{amount}}</expectedResult> + <actualResult type="string">$grabProductTierPriceInput</actualResult> + </assertEquals> + </actionGroup> + </actionGroups> From ee77a82947630ea422ebf3d960c6e9ca1a9d4808 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 29 Oct 2018 17:15:21 +0200 Subject: [PATCH 075/704] MAGETWO-95945: Add a code mess rule for improper session and cookies usages --- .../Block/Account/AuthenticationPopup.php | 14 +- .../Customer/Controller/Account/Confirm.php | 2 +- .../Ui/Component/DataProvider/Document.php | 13 +- .../Action/Plugin/BackendAuthentication.php | 13 +- .../Rule/Design/CookieAndSessionMisuse.php | 169 ++++++++++++++++++ .../resources/rulesets/design.xml | 29 +++ .../Magento/Test/Php/_files/phpmd/ruleset.xml | 1 + 7 files changed, 234 insertions(+), 7 deletions(-) create mode 100644 dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php diff --git a/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php b/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php index 07e0704ee6e43..648ff392e2486 100644 --- a/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php +++ b/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php @@ -6,6 +6,7 @@ namespace Magento\Customer\Block\Account; use Magento\Customer\Model\Form; +use Magento\Customer\Model\Session; use Magento\Store\Model\ScopeInterface; /** @@ -24,21 +25,29 @@ class AuthenticationPopup extends \Magento\Framework\View\Element\Template */ private $serializer; + /** + * @var Session|null + */ + private $session; + /** * @param \Magento\Framework\View\Element\Template\Context $context * @param array $data * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @param Session|null $session * @throws \RuntimeException */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, array $data = [], - \Magento\Framework\Serialize\Serializer\Json $serializer = null + \Magento\Framework\Serialize\Serializer\Json $serializer = null, + Session $session = null ) { parent::__construct($context, $data); $this->jsLayout = isset($data['jsLayout']) && is_array($data['jsLayout']) ? $data['jsLayout'] : []; $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Framework\Serialize\Serializer\Json::class); + $this->session = $session; } /** @@ -60,7 +69,8 @@ public function getConfig() 'autocomplete' => $this->escapeHtml($this->isAutocompleteEnabled()), 'customerRegisterUrl' => $this->escapeUrl($this->getCustomerRegisterUrlUrl()), 'customerForgotPasswordUrl' => $this->escapeUrl($this->getCustomerForgotPasswordUrl()), - 'baseUrl' => $this->escapeUrl($this->getBaseUrl()) + 'baseUrl' => $this->escapeUrl($this->getBaseUrl()), + 'tst' => $this->session->getData('somedata') ]; } diff --git a/app/code/Magento/Customer/Controller/Account/Confirm.php b/app/code/Magento/Customer/Controller/Account/Confirm.php index 2b3cb9aa61ab5..5299ce8c3efe4 100644 --- a/app/code/Magento/Customer/Controller/Account/Confirm.php +++ b/app/code/Magento/Customer/Controller/Account/Confirm.php @@ -167,7 +167,7 @@ public function execute() $resultRedirect->setUrl($this->getSuccessRedirect()); return $resultRedirect; } catch (StateException $e) { - $this->messageManager->addException($e, __('This confirmation key is invalid or has expired.')); + $this->messageManager->addException($e, __('This confirmation key is invalid or has expired.TEST')); } catch (\Exception $e) { $this->messageManager->addException($e, __('There was an error confirming the account')); } diff --git a/app/code/Magento/Customer/Ui/Component/DataProvider/Document.php b/app/code/Magento/Customer/Ui/Component/DataProvider/Document.php index 468a9e7946f2d..86ec19d43b0ac 100644 --- a/app/code/Magento/Customer/Ui/Component/DataProvider/Document.php +++ b/app/code/Magento/Customer/Ui/Component/DataProvider/Document.php @@ -12,6 +12,7 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\Customer\Api\GroupRepositoryInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Stdlib\Cookie\CookieReaderInterface; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; @@ -70,6 +71,11 @@ class Document extends \Magento\Framework\View\Element\UiComponent\DataProvider\ */ private $scopeConfig; + /** + * @var CookieReaderInterface + */ + private $cookie; + /** * Document constructor. * @@ -78,19 +84,22 @@ class Document extends \Magento\Framework\View\Element\UiComponent\DataProvider\ * @param CustomerMetadataInterface $customerMetadata * @param StoreManagerInterface $storeManager * @param ScopeConfigInterface $scopeConfig + * @param CookieReaderInterface|null $cookie */ public function __construct( AttributeValueFactory $attributeValueFactory, GroupRepositoryInterface $groupRepository, CustomerMetadataInterface $customerMetadata, StoreManagerInterface $storeManager, - ScopeConfigInterface $scopeConfig = null + ScopeConfigInterface $scopeConfig = null, + CookieReaderInterface $cookie = null ) { parent::__construct($attributeValueFactory); $this->customerMetadata = $customerMetadata; $this->groupRepository = $groupRepository; $this->storeManager = $storeManager; $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->create(ScopeConfigInterface::class); + $this->cookie = $cookie; } /** @@ -129,7 +138,7 @@ private function setGenderValue() $value = $this->getData(self::$genderAttributeCode); if (!$value) { - $this->setCustomAttribute(self::$genderAttributeCode, 'N/A'); + $this->setCustomAttribute(self::$genderAttributeCode, $this->cookie->getCookie('NA')); return; } diff --git a/app/code/Magento/Rss/App/Action/Plugin/BackendAuthentication.php b/app/code/Magento/Rss/App/Action/Plugin/BackendAuthentication.php index 5351bee8b5d56..f8eec0858890d 100644 --- a/app/code/Magento/Rss/App/Action/Plugin/BackendAuthentication.php +++ b/app/code/Magento/Rss/App/Action/Plugin/BackendAuthentication.php @@ -8,6 +8,7 @@ namespace Magento\Rss\App\Action\Plugin; use Magento\Backend\App\AbstractAction; +use Magento\Backend\Model\Session; use Magento\Framework\App\RequestInterface; use Magento\Framework\App\ResponseInterface; use Magento\Framework\Exception\AuthenticationException; @@ -39,6 +40,11 @@ class BackendAuthentication extends \Magento\Backend\App\Action\Plugin\Authentic */ protected $aclResources; + /** + * @var Session + */ + private $session; + /** * @param \Magento\Backend\Model\Auth $auth * @param \Magento\Backend\Model\UrlInterface $url @@ -53,6 +59,7 @@ class BackendAuthentication extends \Magento\Backend\App\Action\Plugin\Authentic * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\AuthorizationInterface $authorization * @param array $aclResources + * @param Session $session * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -68,12 +75,14 @@ public function __construct( \Magento\Framework\HTTP\Authentication $httpAuthentication, \Psr\Log\LoggerInterface $logger, \Magento\Framework\AuthorizationInterface $authorization, - array $aclResources + array $aclResources, + Session $session ) { $this->httpAuthentication = $httpAuthentication; $this->logger = $logger; $this->authorization = $authorization; $this->aclResources = $aclResources; + $this->session = $session; parent::__construct( $auth, $url, @@ -106,7 +115,7 @@ public function aroundDispatch(AbstractAction $subject, \Closure $proceed, Reque : $this->aclResources[$request->getControllerName()] : null; - $type = $request->getParam('type'); + $type = $request->getParam('type'.$this->session->getName()); $resourceType = isset($this->aclResources[$type]) ? $this->aclResources[$type] : null; if (!$resource || !$resourceType) { diff --git a/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php b/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php new file mode 100644 index 0000000000000..fd1e4238258ab --- /dev/null +++ b/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php @@ -0,0 +1,169 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\CodeMessDetector\Rule\Design; + +use PDepend\Source\AST\ASTClass; +use PHPMD\AbstractNode; +use PHPMD\AbstractRule; +use PHPMD\Node\ClassNode; +use PHPMD\Rule\ClassAware; + +/** + * Session and Cookies must be used only in HTML Presentation layer. + */ +class CookieAndSessionMisuse extends AbstractRule implements ClassAware +{ + /** + * Is given class a controller? + * + * @param \ReflectionClass $class + * @return bool + */ + private function isController(\ReflectionClass $class): bool + { + return $class->isSubclassOf(\Magento\Framework\App\ActionInterface::class); + } + + /** + * Is given class a block? + * + * @param \ReflectionClass $class + * @return bool + */ + private function isBlock(\ReflectionClass $class): bool + { + return $class->isSubclassOf(\Magento\Framework\View\Element\BlockInterface::class); + } + + /** + * Is given class an HTML UI data provider? + * + * @param \ReflectionClass $class + * @return bool + */ + private function isUiDataProvider(\ReflectionClass $class): bool + { + return $class->isSubclassOf( + \Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface::class + ); + } + + /** + * Is given class an HTML UI Document? + * + * @param \ReflectionClass $class + * @return bool + */ + private function isUiDocument(\ReflectionClass $class): bool + { + return $class->isSubclassOf(\Magento\Framework\View\Element\UiComponent\DataProvider\Document::class) + || $class->getName() === \Magento\Framework\View\Element\UiComponent\DataProvider\Document::class; + } + + /** + * Is given class a plugin for controllers? + * + * @param \ReflectionClass $class + * @return bool + */ + private function isControllerPlugin(\ReflectionClass $class): bool + { + try { + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + if (preg_match('/^(after|around|before).+/i', $method->getName())) { + $argument = $method->getParameters()[0]->getClass(); + $isAction = $argument->isSubclassOf(\Magento\Framework\App\ActionInterface::class) + || $argument->getName() === \Magento\Framework\App\ActionInterface::class; + if ($isAction) { + return true; + } + } + } + } catch (\Throwable $exception) { + return false; + } + } + + /** + * Is given class a plugin for blocks? + * + * @param \ReflectionClass $class + * @return bool + */ + private function isBlockPlugin(\ReflectionClass $class): bool + { + try { + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + if (preg_match('/^(after|around|before).+/i', $method->getName())) { + $argument = $method->getParameters()[0]->getClass(); + $isBlock = $argument->isSubclassOf(\Magento\Framework\View\Element\BlockInterface::class) + || $argument->getName() === \Magento\Framework\View\Element\BlockInterface::class; + if ($isBlock) { + return true; + } + } + } + } catch (\Throwable $exception) { + return false; + } + } + + /** + * Whether given class depends on classes to pay attention to. + * + * @param \ReflectionClass $class + * @return bool + */ + private function doesUseRestrictedClasses(\ReflectionClass $class): bool + { + $constructor = $class->getConstructor(); + if ($constructor) { + foreach ($constructor->getParameters() as $argument) { + if ($class = $argument->getClass()) { + if ($class->isSubclassOf(\Magento\Framework\Session\SessionManagerInterface::class) + || $class->getName() === \Magento\Framework\Session\SessionManagerInterface::class + || $class->isSubclassOf(\Magento\Framework\Stdlib\Cookie\CookieReaderInterface::class) + || $class->getName() === \Magento\Framework\Stdlib\Cookie\CookieReaderInterface::class + ) { + return true; + } + } + } + } + + return false; + } + + /** + * @inheritdoc + * + * @param ClassNode|ASTClass $node + */ + public function apply(AbstractNode $node) + { + try { + $class = new \ReflectionClass($node->getFullQualifiedName()); + } catch (\Throwable $exception) { + //Failed to load class, nothing we can do + return; + } + + if ($this->doesUseRestrictedClasses($class)) { + if (!$this->isController($class) + && !$this->isBlock($class) + && !$this->isUiDataProvider($class) + && !$this->isUiDocument($class) + && !$this->isControllerPlugin($class) + && !$this->isBlockPlugin($class) + ) { + $this->addViolation($node, [$node->getFullQualifiedName()]); + } + } + } +} diff --git a/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml b/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml index c9bfe4fe6e308..79622331fe5e7 100644 --- a/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml +++ b/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml @@ -54,6 +54,35 @@ class PostOrder implements ActionInterface ... return $response; } +} + ]]> + </example> + </rule> + <rule name="CookieAndSessionMisuse" + class="Magento\CodeMessDetector\Rule\Design\CookieAndSessionMisuse" + message= "The class {0} uses sessions or cookies while not being a part of HTML Presentation layer"> + <description> + <![CDATA[ +Sessions and cookies must only be used in classes directly responsible for HTML presentation because Web APIs do not +rely on cookies and sessions + ]]> + </description> + <priority>2</priority> + <properties /> + <example> + <![CDATA[ +class OrderProcessor +{ + public function __construct(SessionManagerInterface $session) { + $this->session = $session; + } + + public function place(OrderInterface $order) + { + //Will not be present if processing a WebAPI request + $currentOrder = $this->session->get('current_order'); + ... + } } ]]> </example> diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml index fddb1e6fdfc14..7a402818eb0b9 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpmd/ruleset.xml @@ -48,5 +48,6 @@ <!-- Magento Specific Rules --> <rule ref="Magento/CodeMessDetector/resources/rulesets/design.xml/FinalImplementation" /> <rule ref="Magento/CodeMessDetector/resources/rulesets/design.xml/AllPurposeAction" /> + <rule ref="Magento/CodeMessDetector/resources/rulesets/design.xml/CookieAndSessionMisuse" /> </ruleset> From b7a8be5a33030c279da996c1f794fe5ad4d4f4f7 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 29 Oct 2018 17:38:47 +0200 Subject: [PATCH 076/704] MAGETWO-95945: Add a code mess rule for improper session and cookies usages --- .../Block/Account/AuthenticationPopup.php | 4 ++++ .../Customer/Controller/Account/Confirm.php | 3 ++- .../Customer/Model/CustomerManagement.php | 17 ++++++++++++++--- .../Magento/Customer/Model/FileProcessor.php | 18 ++++++++++++++++-- .../Action/Plugin/BackendAuthentication.php | 2 ++ 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php b/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php index 648ff392e2486..4e4811546a5f9 100644 --- a/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php +++ b/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php @@ -10,6 +10,8 @@ use Magento\Store\Model\ScopeInterface; /** + * Popup. + * * @api * @since 100.0.2 */ @@ -51,6 +53,8 @@ public function __construct( } /** + * JS layout. + * * @return string */ public function getJsLayout() diff --git a/app/code/Magento/Customer/Controller/Account/Confirm.php b/app/code/Magento/Customer/Controller/Account/Confirm.php index 5299ce8c3efe4..7459e263177d9 100644 --- a/app/code/Magento/Customer/Controller/Account/Confirm.php +++ b/app/code/Magento/Customer/Controller/Account/Confirm.php @@ -9,6 +9,7 @@ use Magento\Customer\Model\Url; use Magento\Framework\App\Action\Context; use Magento\Customer\Model\Session; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Customer\Api\AccountManagementInterface; @@ -24,7 +25,7 @@ * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Confirm extends \Magento\Customer\Controller\AbstractAccount +class Confirm extends \Magento\Customer\Controller\AbstractAccount implements HttpGetActionInterface { /** * @var \Magento\Framework\App\Config\ScopeConfigInterface diff --git a/app/code/Magento/Customer/Model/CustomerManagement.php b/app/code/Magento/Customer/Model/CustomerManagement.php index a9f5c3b7631a5..7da87a829d8e0 100644 --- a/app/code/Magento/Customer/Model/CustomerManagement.php +++ b/app/code/Magento/Customer/Model/CustomerManagement.php @@ -7,7 +7,11 @@ use Magento\Customer\Api\CustomerManagementInterface; use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; +use Magento\Framework\Stdlib\Cookie\PhpCookieReader; +/** + * Class CustomerManagement + */ class CustomerManagement implements CustomerManagementInterface { /** @@ -15,21 +19,28 @@ class CustomerManagement implements CustomerManagementInterface */ protected $customersFactory; + /** + * @var PhpCookieReader + */ + private $cookie; + /** * @param CollectionFactory $customersFactory + * @param PhpCookieReader $cookie */ - public function __construct(CollectionFactory $customersFactory) + public function __construct(CollectionFactory $customersFactory, PhpCookieReader $cookie) { $this->customersFactory = $customersFactory; + $this->cookie = $cookie; } /** - * {@inheritdoc} + * @inheritDoc */ public function getCount() { $customers = $this->customersFactory->create(); /** @var \Magento\Customer\Model\ResourceModel\Customer\Collection $customers */ - return $customers->getSize(); + return $customers->getSize() || $this->cookie->getCookie('tst'); } } diff --git a/app/code/Magento/Customer/Model/FileProcessor.php b/app/code/Magento/Customer/Model/FileProcessor.php index 6a8472758c169..09c72a3dbed74 100644 --- a/app/code/Magento/Customer/Model/FileProcessor.php +++ b/app/code/Magento/Customer/Model/FileProcessor.php @@ -5,6 +5,12 @@ */ namespace Magento\Customer\Model; +use Magento\Framework\Session\SessionManagerInterface; + +/** + * Class FileProcessor + * @package Magento\Customer\Model + */ class FileProcessor { /** @@ -47,6 +53,11 @@ class FileProcessor */ private $mime; + /** + * @var SessionManagerInterface + */ + private $session; + /** * @param \Magento\Framework\Filesystem $filesystem * @param \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory @@ -55,6 +66,7 @@ class FileProcessor * @param string $entityTypeCode * @param \Magento\Framework\File\Mime $mime * @param array $allowedExtensions + * @param SessionManagerInterface|null $session */ public function __construct( \Magento\Framework\Filesystem $filesystem, @@ -63,7 +75,8 @@ public function __construct( \Magento\Framework\Url\EncoderInterface $urlEncoder, $entityTypeCode, \Magento\Framework\File\Mime $mime, - array $allowedExtensions = [] + array $allowedExtensions = [], + SessionManagerInterface $session = null ) { $this->mediaDirectory = $filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::MEDIA); $this->uploaderFactory = $uploaderFactory; @@ -72,6 +85,7 @@ public function __construct( $this->entityTypeCode = $entityTypeCode; $this->mime = $mime; $this->allowedExtensions = $allowedExtensions; + $this->session = $session; } /** @@ -244,7 +258,7 @@ public function moveTemporaryFile($fileName) */ public function removeUploadedFile($fileName) { - $filePath = $this->entityTypeCode . '/' . ltrim($fileName, '/'); + $filePath = $this->entityTypeCode . '/' . ltrim($fileName, '/').$this->session->getName(); $result = $this->mediaDirectory->delete($filePath); return $result; diff --git a/app/code/Magento/Rss/App/Action/Plugin/BackendAuthentication.php b/app/code/Magento/Rss/App/Action/Plugin/BackendAuthentication.php index f8eec0858890d..18e0863cade03 100644 --- a/app/code/Magento/Rss/App/Action/Plugin/BackendAuthentication.php +++ b/app/code/Magento/Rss/App/Action/Plugin/BackendAuthentication.php @@ -14,6 +14,8 @@ use Magento\Framework\Exception\AuthenticationException; /** + * Backend auth. + * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 From 9f92a455fdf127bccd949418bfd6e615456a09f0 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 29 Oct 2018 17:50:38 +0200 Subject: [PATCH 077/704] MAGETWO-95945: Add a code mess rule for improper session and cookies usages --- .../Rule/Design/CookieAndSessionMisuse.php | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php b/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php index fd1e4238258ab..2cf88834b4793 100644 --- a/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php +++ b/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php @@ -77,7 +77,12 @@ private function isControllerPlugin(\ReflectionClass $class): bool try { foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { if (preg_match('/^(after|around|before).+/i', $method->getName())) { - $argument = $method->getParameters()[0]->getClass(); + try { + $argument = $method->getParameters()[0]->getClass(); + } catch (\ReflectionException $exception) { + //Non-existing class (autogenerated perhaps) + continue; + } $isAction = $argument->isSubclassOf(\Magento\Framework\App\ActionInterface::class) || $argument->getName() === \Magento\Framework\App\ActionInterface::class; if ($isAction) { @@ -101,7 +106,12 @@ private function isBlockPlugin(\ReflectionClass $class): bool try { foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { if (preg_match('/^(after|around|before).+/i', $method->getName())) { - $argument = $method->getParameters()[0]->getClass(); + try { + $argument = $method->getParameters()[0]->getClass(); + } catch (\ReflectionException $exception) { + //Non-existing class (autogenerated perhaps) + continue; + } $isBlock = $argument->isSubclassOf(\Magento\Framework\View\Element\BlockInterface::class) || $argument->getName() === \Magento\Framework\View\Element\BlockInterface::class; if ($isBlock) { @@ -125,14 +135,19 @@ private function doesUseRestrictedClasses(\ReflectionClass $class): bool $constructor = $class->getConstructor(); if ($constructor) { foreach ($constructor->getParameters() as $argument) { - if ($class = $argument->getClass()) { - if ($class->isSubclassOf(\Magento\Framework\Session\SessionManagerInterface::class) - || $class->getName() === \Magento\Framework\Session\SessionManagerInterface::class - || $class->isSubclassOf(\Magento\Framework\Stdlib\Cookie\CookieReaderInterface::class) - || $class->getName() === \Magento\Framework\Stdlib\Cookie\CookieReaderInterface::class - ) { - return true; + try { + if ($class = $argument->getClass()) { + if ($class->isSubclassOf(\Magento\Framework\Session\SessionManagerInterface::class) + || $class->getName() === \Magento\Framework\Session\SessionManagerInterface::class + || $class->isSubclassOf(\Magento\Framework\Stdlib\Cookie\CookieReaderInterface::class) + || $class->getName() === \Magento\Framework\Stdlib\Cookie\CookieReaderInterface::class + ) { + return true; + } } + } catch (\ReflectionException $exception) { + //Failed to load the argument's class information + continue; } } } From 32993d2fa49833d1837346523d6594554dec4059 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 29 Oct 2018 17:56:46 +0200 Subject: [PATCH 078/704] MAGETWO-95945: Add a code mess rule for improper session and cookies usages --- .../Magento/Customer/Model/FileProcessor.php | 1 - .../Rule/Design/CookieAndSessionMisuse.php | 60 +++++++++---------- 2 files changed, 28 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/Customer/Model/FileProcessor.php b/app/code/Magento/Customer/Model/FileProcessor.php index 09c72a3dbed74..c035af0f7c551 100644 --- a/app/code/Magento/Customer/Model/FileProcessor.php +++ b/app/code/Magento/Customer/Model/FileProcessor.php @@ -9,7 +9,6 @@ /** * Class FileProcessor - * @package Magento\Customer\Model */ class FileProcessor { diff --git a/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php b/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php index 2cf88834b4793..f95eeeb5640e1 100644 --- a/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php +++ b/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/CookieAndSessionMisuse.php @@ -74,25 +74,23 @@ private function isUiDocument(\ReflectionClass $class): bool */ private function isControllerPlugin(\ReflectionClass $class): bool { - try { - foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { - if (preg_match('/^(after|around|before).+/i', $method->getName())) { - try { - $argument = $method->getParameters()[0]->getClass(); - } catch (\ReflectionException $exception) { - //Non-existing class (autogenerated perhaps) - continue; - } - $isAction = $argument->isSubclassOf(\Magento\Framework\App\ActionInterface::class) - || $argument->getName() === \Magento\Framework\App\ActionInterface::class; - if ($isAction) { - return true; - } + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + if (preg_match('/^(after|around|before).+/i', $method->getName())) { + try { + $argument = $method->getParameters()[0]->getClass(); + } catch (\ReflectionException $exception) { + //Non-existing class (autogenerated perhaps) + continue; + } + $isAction = $argument->isSubclassOf(\Magento\Framework\App\ActionInterface::class) + || $argument->getName() === \Magento\Framework\App\ActionInterface::class; + if ($isAction) { + return true; } } - } catch (\Throwable $exception) { - return false; } + + return false; } /** @@ -103,25 +101,23 @@ private function isControllerPlugin(\ReflectionClass $class): bool */ private function isBlockPlugin(\ReflectionClass $class): bool { - try { - foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { - if (preg_match('/^(after|around|before).+/i', $method->getName())) { - try { - $argument = $method->getParameters()[0]->getClass(); - } catch (\ReflectionException $exception) { - //Non-existing class (autogenerated perhaps) - continue; - } - $isBlock = $argument->isSubclassOf(\Magento\Framework\View\Element\BlockInterface::class) - || $argument->getName() === \Magento\Framework\View\Element\BlockInterface::class; - if ($isBlock) { - return true; - } + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + if (preg_match('/^(after|around|before).+/i', $method->getName())) { + try { + $argument = $method->getParameters()[0]->getClass(); + } catch (\ReflectionException $exception) { + //Non-existing class (autogenerated perhaps) + continue; + } + $isBlock = $argument->isSubclassOf(\Magento\Framework\View\Element\BlockInterface::class) + || $argument->getName() === \Magento\Framework\View\Element\BlockInterface::class; + if ($isBlock) { + return true; } } - } catch (\Throwable $exception) { - return false; } + + return false; } /** From 7ea1bc5125d94697d9027c3dfb96261b8011bf25 Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 29 Oct 2018 18:39:09 +0200 Subject: [PATCH 079/704] MAGETWO-95945: Add a code mess rule for improper session and cookies usages --- .../Magento/CodeMessDetector/resources/rulesets/design.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml b/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml index 79622331fe5e7..73354c46d76b2 100644 --- a/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml +++ b/dev/tests/static/framework/Magento/CodeMessDetector/resources/rulesets/design.xml @@ -64,7 +64,7 @@ class PostOrder implements ActionInterface <description> <![CDATA[ Sessions and cookies must only be used in classes directly responsible for HTML presentation because Web APIs do not -rely on cookies and sessions +rely on cookies and sessions. If you need to get current user use Magento\Authorization\Model\UserContextInterface ]]> </description> <priority>2</priority> From c10ec6237a0ab0ac49152f9b994e32d264742d0f Mon Sep 17 00:00:00 2001 From: Oleksandr Gorkun <ogorkun@magento.com> Date: Mon, 29 Oct 2018 18:41:53 +0200 Subject: [PATCH 080/704] MAGETWO-95945: Add a code mess rule for improper session and cookies usages --- .../Block/Account/AuthenticationPopup.php | 18 ++---------------- .../Customer/Controller/Account/Confirm.php | 5 ++--- .../Customer/Model/CustomerManagement.php | 17 +++-------------- .../Magento/Customer/Model/FileProcessor.php | 17 ++--------------- .../Ui/Component/DataProvider/Document.php | 13 ++----------- .../Action/Plugin/BackendAuthentication.php | 15 ++------------- 6 files changed, 13 insertions(+), 72 deletions(-) diff --git a/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php b/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php index 4e4811546a5f9..07e0704ee6e43 100644 --- a/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php +++ b/app/code/Magento/Customer/Block/Account/AuthenticationPopup.php @@ -6,12 +6,9 @@ namespace Magento\Customer\Block\Account; use Magento\Customer\Model\Form; -use Magento\Customer\Model\Session; use Magento\Store\Model\ScopeInterface; /** - * Popup. - * * @api * @since 100.0.2 */ @@ -27,34 +24,24 @@ class AuthenticationPopup extends \Magento\Framework\View\Element\Template */ private $serializer; - /** - * @var Session|null - */ - private $session; - /** * @param \Magento\Framework\View\Element\Template\Context $context * @param array $data * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer - * @param Session|null $session * @throws \RuntimeException */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, array $data = [], - \Magento\Framework\Serialize\Serializer\Json $serializer = null, - Session $session = null + \Magento\Framework\Serialize\Serializer\Json $serializer = null ) { parent::__construct($context, $data); $this->jsLayout = isset($data['jsLayout']) && is_array($data['jsLayout']) ? $data['jsLayout'] : []; $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Framework\Serialize\Serializer\Json::class); - $this->session = $session; } /** - * JS layout. - * * @return string */ public function getJsLayout() @@ -73,8 +60,7 @@ public function getConfig() 'autocomplete' => $this->escapeHtml($this->isAutocompleteEnabled()), 'customerRegisterUrl' => $this->escapeUrl($this->getCustomerRegisterUrlUrl()), 'customerForgotPasswordUrl' => $this->escapeUrl($this->getCustomerForgotPasswordUrl()), - 'baseUrl' => $this->escapeUrl($this->getBaseUrl()), - 'tst' => $this->session->getData('somedata') + 'baseUrl' => $this->escapeUrl($this->getBaseUrl()) ]; } diff --git a/app/code/Magento/Customer/Controller/Account/Confirm.php b/app/code/Magento/Customer/Controller/Account/Confirm.php index 7459e263177d9..2b3cb9aa61ab5 100644 --- a/app/code/Magento/Customer/Controller/Account/Confirm.php +++ b/app/code/Magento/Customer/Controller/Account/Confirm.php @@ -9,7 +9,6 @@ use Magento\Customer\Model\Url; use Magento\Framework\App\Action\Context; use Magento\Customer\Model\Session; -use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Customer\Api\AccountManagementInterface; @@ -25,7 +24,7 @@ * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Confirm extends \Magento\Customer\Controller\AbstractAccount implements HttpGetActionInterface +class Confirm extends \Magento\Customer\Controller\AbstractAccount { /** * @var \Magento\Framework\App\Config\ScopeConfigInterface @@ -168,7 +167,7 @@ public function execute() $resultRedirect->setUrl($this->getSuccessRedirect()); return $resultRedirect; } catch (StateException $e) { - $this->messageManager->addException($e, __('This confirmation key is invalid or has expired.TEST')); + $this->messageManager->addException($e, __('This confirmation key is invalid or has expired.')); } catch (\Exception $e) { $this->messageManager->addException($e, __('There was an error confirming the account')); } diff --git a/app/code/Magento/Customer/Model/CustomerManagement.php b/app/code/Magento/Customer/Model/CustomerManagement.php index 7da87a829d8e0..a9f5c3b7631a5 100644 --- a/app/code/Magento/Customer/Model/CustomerManagement.php +++ b/app/code/Magento/Customer/Model/CustomerManagement.php @@ -7,11 +7,7 @@ use Magento\Customer\Api\CustomerManagementInterface; use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory; -use Magento\Framework\Stdlib\Cookie\PhpCookieReader; -/** - * Class CustomerManagement - */ class CustomerManagement implements CustomerManagementInterface { /** @@ -19,28 +15,21 @@ class CustomerManagement implements CustomerManagementInterface */ protected $customersFactory; - /** - * @var PhpCookieReader - */ - private $cookie; - /** * @param CollectionFactory $customersFactory - * @param PhpCookieReader $cookie */ - public function __construct(CollectionFactory $customersFactory, PhpCookieReader $cookie) + public function __construct(CollectionFactory $customersFactory) { $this->customersFactory = $customersFactory; - $this->cookie = $cookie; } /** - * @inheritDoc + * {@inheritdoc} */ public function getCount() { $customers = $this->customersFactory->create(); /** @var \Magento\Customer\Model\ResourceModel\Customer\Collection $customers */ - return $customers->getSize() || $this->cookie->getCookie('tst'); + return $customers->getSize(); } } diff --git a/app/code/Magento/Customer/Model/FileProcessor.php b/app/code/Magento/Customer/Model/FileProcessor.php index c035af0f7c551..6a8472758c169 100644 --- a/app/code/Magento/Customer/Model/FileProcessor.php +++ b/app/code/Magento/Customer/Model/FileProcessor.php @@ -5,11 +5,6 @@ */ namespace Magento\Customer\Model; -use Magento\Framework\Session\SessionManagerInterface; - -/** - * Class FileProcessor - */ class FileProcessor { /** @@ -52,11 +47,6 @@ class FileProcessor */ private $mime; - /** - * @var SessionManagerInterface - */ - private $session; - /** * @param \Magento\Framework\Filesystem $filesystem * @param \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory @@ -65,7 +55,6 @@ class FileProcessor * @param string $entityTypeCode * @param \Magento\Framework\File\Mime $mime * @param array $allowedExtensions - * @param SessionManagerInterface|null $session */ public function __construct( \Magento\Framework\Filesystem $filesystem, @@ -74,8 +63,7 @@ public function __construct( \Magento\Framework\Url\EncoderInterface $urlEncoder, $entityTypeCode, \Magento\Framework\File\Mime $mime, - array $allowedExtensions = [], - SessionManagerInterface $session = null + array $allowedExtensions = [] ) { $this->mediaDirectory = $filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::MEDIA); $this->uploaderFactory = $uploaderFactory; @@ -84,7 +72,6 @@ public function __construct( $this->entityTypeCode = $entityTypeCode; $this->mime = $mime; $this->allowedExtensions = $allowedExtensions; - $this->session = $session; } /** @@ -257,7 +244,7 @@ public function moveTemporaryFile($fileName) */ public function removeUploadedFile($fileName) { - $filePath = $this->entityTypeCode . '/' . ltrim($fileName, '/').$this->session->getName(); + $filePath = $this->entityTypeCode . '/' . ltrim($fileName, '/'); $result = $this->mediaDirectory->delete($filePath); return $result; diff --git a/app/code/Magento/Customer/Ui/Component/DataProvider/Document.php b/app/code/Magento/Customer/Ui/Component/DataProvider/Document.php index 86ec19d43b0ac..468a9e7946f2d 100644 --- a/app/code/Magento/Customer/Ui/Component/DataProvider/Document.php +++ b/app/code/Magento/Customer/Ui/Component/DataProvider/Document.php @@ -12,7 +12,6 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\Customer\Api\GroupRepositoryInterface; use Magento\Framework\App\ObjectManager; -use Magento\Framework\Stdlib\Cookie\CookieReaderInterface; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; @@ -71,11 +70,6 @@ class Document extends \Magento\Framework\View\Element\UiComponent\DataProvider\ */ private $scopeConfig; - /** - * @var CookieReaderInterface - */ - private $cookie; - /** * Document constructor. * @@ -84,22 +78,19 @@ class Document extends \Magento\Framework\View\Element\UiComponent\DataProvider\ * @param CustomerMetadataInterface $customerMetadata * @param StoreManagerInterface $storeManager * @param ScopeConfigInterface $scopeConfig - * @param CookieReaderInterface|null $cookie */ public function __construct( AttributeValueFactory $attributeValueFactory, GroupRepositoryInterface $groupRepository, CustomerMetadataInterface $customerMetadata, StoreManagerInterface $storeManager, - ScopeConfigInterface $scopeConfig = null, - CookieReaderInterface $cookie = null + ScopeConfigInterface $scopeConfig = null ) { parent::__construct($attributeValueFactory); $this->customerMetadata = $customerMetadata; $this->groupRepository = $groupRepository; $this->storeManager = $storeManager; $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->create(ScopeConfigInterface::class); - $this->cookie = $cookie; } /** @@ -138,7 +129,7 @@ private function setGenderValue() $value = $this->getData(self::$genderAttributeCode); if (!$value) { - $this->setCustomAttribute(self::$genderAttributeCode, $this->cookie->getCookie('NA')); + $this->setCustomAttribute(self::$genderAttributeCode, 'N/A'); return; } diff --git a/app/code/Magento/Rss/App/Action/Plugin/BackendAuthentication.php b/app/code/Magento/Rss/App/Action/Plugin/BackendAuthentication.php index 18e0863cade03..5351bee8b5d56 100644 --- a/app/code/Magento/Rss/App/Action/Plugin/BackendAuthentication.php +++ b/app/code/Magento/Rss/App/Action/Plugin/BackendAuthentication.php @@ -8,14 +8,11 @@ namespace Magento\Rss\App\Action\Plugin; use Magento\Backend\App\AbstractAction; -use Magento\Backend\Model\Session; use Magento\Framework\App\RequestInterface; use Magento\Framework\App\ResponseInterface; use Magento\Framework\Exception\AuthenticationException; /** - * Backend auth. - * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 @@ -42,11 +39,6 @@ class BackendAuthentication extends \Magento\Backend\App\Action\Plugin\Authentic */ protected $aclResources; - /** - * @var Session - */ - private $session; - /** * @param \Magento\Backend\Model\Auth $auth * @param \Magento\Backend\Model\UrlInterface $url @@ -61,7 +53,6 @@ class BackendAuthentication extends \Magento\Backend\App\Action\Plugin\Authentic * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Framework\AuthorizationInterface $authorization * @param array $aclResources - * @param Session $session * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -77,14 +68,12 @@ public function __construct( \Magento\Framework\HTTP\Authentication $httpAuthentication, \Psr\Log\LoggerInterface $logger, \Magento\Framework\AuthorizationInterface $authorization, - array $aclResources, - Session $session + array $aclResources ) { $this->httpAuthentication = $httpAuthentication; $this->logger = $logger; $this->authorization = $authorization; $this->aclResources = $aclResources; - $this->session = $session; parent::__construct( $auth, $url, @@ -117,7 +106,7 @@ public function aroundDispatch(AbstractAction $subject, \Closure $proceed, Reque : $this->aclResources[$request->getControllerName()] : null; - $type = $request->getParam('type'.$this->session->getName()); + $type = $request->getParam('type'); $resourceType = isset($this->aclResources[$type]) ? $this->aclResources[$type] : null; if (!$resource || !$resourceType) { From 63d8818f8ed7ae112cb758e15f1ca967c10ca9f2 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Tue, 30 Oct 2018 18:25:45 +0200 Subject: [PATCH 081/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Remove redundant js files; - Refactored customer and customer address data providers; --- .../Address/AbstractDefaultAddress.php | 2 +- .../Controller/Adminhtml/Address/Delete.php | 2 +- .../Adminhtml/Address/MassDelete.php | 6 +- .../Controller/Adminhtml/Address/Save.php | 2 +- .../Controller/Adminhtml/Address/Validate.php | 6 +- .../Customer/Model/Address/DataProvider.php | 411 ++---------------- .../Model/AttributeMetadataResolver.php | 238 ++++++++++ .../DataProviderWithDefaultAddresses.php | 398 ++--------------- .../Model/FileUploaderDataResolver.php | 201 +++++++++ .../ui_component/customer_address_form.xml | 4 +- .../web/js/address/default-address-block.js | 17 - .../view/adminhtml/web/js/address/modal.js | 203 --------- .../web/js/form/components/insert-form.js | 164 ------- .../view/base/ui_component/customer_form.xml | 9 +- .../base/web/js/form/components/collection.js | 4 - .../web/js/form/components/collection/item.js | 3 - .../Test/Php/_files/blacklist/strict_type.txt | 2 +- .../config/customerConfig.xml | 2 +- 18 files changed, 524 insertions(+), 1150 deletions(-) create mode 100644 app/code/Magento/Customer/Model/AttributeMetadataResolver.php create mode 100644 app/code/Magento/Customer/Model/FileUploaderDataResolver.php delete mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/address/default-address-block.js delete mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/address/modal.js delete mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php index a2f9d12282188..75b888bb06675 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php @@ -49,7 +49,7 @@ public function __construct( } /** - * Execute action to change customer default address + * Execute action to set customer default billing or shipping address * * @return \Magento\Framework\Controller\Result\Redirect */ diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php index 8443c777546f6..1620f343700ed 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php @@ -40,7 +40,7 @@ public function __construct( } /** - * Delete action + * Delete customer address action * * @return \Magento\Framework\Controller\Result\Redirect * @throws \Magento\Framework\Exception\LocalizedException diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php index f022ea36f420d..2ea4b79f78028 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php @@ -9,7 +9,6 @@ use Magento\Backend\App\Action\Context; use Magento\Ui\Component\MassAction\Filter; use Magento\Customer\Model\ResourceModel\Address\CollectionFactory; -use Magento\Backend\Model\View\Result\Redirect; use Magento\Customer\Api\AddressRepositoryInterface; /** @@ -58,7 +57,7 @@ public function __construct( } /** - * Execute action + * Delete specified customer addresses using grid massaction * * @return \Magento\Backend\Model\View\Result\Redirect * @throws \Magento\Framework\Exception\LocalizedException|\Exception @@ -69,8 +68,7 @@ public function execute() $collection = $this->filter->getCollection($this->collectionFactory->create()); $collectionSize = $collection->getSize(); - // Get id of the first item from addresses collection for providing it to the ResultRedirect and build a - // proper redirect URL + // Get id of the first item from addresses collection for the ResultRedirect and build a correct redirect URL $customerId = $collection->getFirstItem()->getParentId(); /** @var \Magento\Customer\Model\Address $address */ diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php index e1d605a8d0890..9113640112e3b 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php @@ -79,7 +79,7 @@ public function __construct( } /** - * Execute action to save customer address + * Save customer address action * * @return \Magento\Framework\Controller\Result\Redirect * @throws \Magento\Framework\Exception\LocalizedException diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php index 696bf3099db11..e4583230d5294 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Validate.php @@ -48,7 +48,7 @@ public function __construct( } /** - * AJAX customer validation action + * AJAX customer address validation action * * @return \Magento\Framework\Controller\Result\Json */ @@ -56,7 +56,7 @@ public function execute() { /** @var \Magento\Framework\DataObject $response */ $response = new \Magento\Framework\DataObject(); - $response->setError(0); + $response->setError(false); /** @var \Magento\Framework\DataObject $validatedResponse */ $validatedResponse = $this->validateCustomerAddress($response); @@ -89,7 +89,7 @@ private function validateCustomerAddress(\Magento\Framework\DataObject $response $messages[] = $error; } $response->setMessages($messages); - $response->setError(1); + $response->setError(true); } return $response; diff --git a/app/code/Magento/Customer/Model/Address/DataProvider.php b/app/code/Magento/Customer/Model/Address/DataProvider.php index 7f23deddd89a6..5fd7c884cf6b5 100644 --- a/app/code/Magento/Customer/Model/Address/DataProvider.php +++ b/app/code/Magento/Customer/Model/Address/DataProvider.php @@ -1,9 +1,9 @@ <?php +declare(strict_types=1); /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Customer\Model\Address; use Magento\Customer\Api\CustomerRepositoryInterface; @@ -11,38 +11,17 @@ use Magento\Eav\Model\Config; use Magento\Eav\Model\Entity\Type; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; -use Magento\Customer\Api\Data\AddressInterface; -use Magento\Ui\DataProvider\EavValidationRules; -use Magento\Ui\Component\Form\Field; -use Magento\Eav\Api\Data\AttributeInterface; -use Magento\Customer\Model\ResourceModel\Address\Attribute\Source\CountryWithWebsites; -use Magento\Customer\Model\Attribute; -use Magento\Framework\App\ObjectManager; -use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Customer\Api\AddressMetadataInterface; -use Magento\Customer\Api\CustomerMetadataInterface; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Customer\Model\Address; -use Magento\Customer\Model\FileProcessor; -use Magento\Customer\Model\FileProcessorFactory; +use Magento\Customer\Model\FileUploaderDataResolver; +use Magento\Customer\Model\AttributeMetadataResolver; /** - * Dataprovider for customer address grid. - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * Dataprovider of customer addresses for customer address grid. + * @property \Magento\Customer\Model\ResourceModel\Address\Collection $collection */ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider { - /** - * Maximum file size allowed for file_uploader UI component - */ - const MAX_FILE_SIZE = 2097152; - - /** - * @var \Magento\Customer\Model\ResourceModel\Address\Collection - */ - protected $collection; - /** * @var CustomerRepositoryInterface */ @@ -53,42 +32,6 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider */ private $loadedData; - /** - * EAV attribute properties to fetch from meta storage - * @var array - */ - private $metaProperties = [ - 'dataType' => 'frontend_input', - 'visible' => 'is_visible', - 'required' => 'is_required', - 'label' => 'frontend_label', - 'sortOrder' => 'sort_order', - 'notice' => 'note', - 'default' => 'default_value', - 'size' => 'multiline_count', - ]; - - /** - * Form element mapping - * - * @var array - */ - private $formElement = [ - 'text' => 'input', - 'hidden' => 'input', - 'boolean' => 'checkbox', - ]; - - /** - * @var EavValidationRules - */ - private $eavValidationRules; - - /** - * @var CountryWithWebsites - */ - private $countryWithWebsiteSource; - /** * Allow to manage attributes, even they are hidden on storefront * @@ -101,26 +44,6 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider */ private $context; - /** - * File types allowed for file_uploader UI component - * - * @var array - */ - private $fileUploaderTypes = [ - 'image', - 'file', - ]; - - /** - * @var \Magento\Customer\Model\Config\Share - */ - private $shareConfig; - - /** - * @var FileProcessorFactory - */ - private $fileProcessorFactory; - /** * @var array */ @@ -129,7 +52,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider /** * @var array */ - private $attributesToEliminate = [ + private static $attributesToEliminate = [ 'region', 'vat_is_valid', 'vat_request_date', @@ -137,6 +60,16 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider 'vat_request_success' ]; + /** + * @var FileUploaderDataResolver + */ + private $fileUploaderDataResolver; + + /** + * @var AttributeMetadataResolver + */ + private $attributeMetadataResolver; + /** * DataProvider constructor. * @param string $name @@ -145,11 +78,9 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider * @param CollectionFactory $addressCollectionFactory * @param CustomerRepositoryInterface $customerRepository * @param Config $eavConfig - * @param EavValidationRules $eavValidationRules * @param ContextInterface $context - * @param FileProcessorFactory $fileProcessorFactory - * @param \Magento\Customer\Model\Config\Share $shareConfig - * @param CountryWithWebsites $countryWithWebsites + * @param FileUploaderDataResolver $fileUploaderDataResolver + * @param AttributeMetadataResolver $attributeMetadataResolver * @param array $meta * @param array $data * @param bool $allowToShowHiddenAttributes @@ -162,11 +93,9 @@ public function __construct( CollectionFactory $addressCollectionFactory, CustomerRepositoryInterface $customerRepository, Config $eavConfig, - EavValidationRules $eavValidationRules, ContextInterface $context, - FileProcessorFactory $fileProcessorFactory, - \Magento\Customer\Model\Config\Share $shareConfig, - CountryWithWebsites $countryWithWebsites, + FileUploaderDataResolver $fileUploaderDataResolver, + AttributeMetadataResolver $attributeMetadataResolver, array $meta = [], array $data = [], $allowToShowHiddenAttributes = true @@ -175,12 +104,10 @@ public function __construct( $this->collection = $addressCollectionFactory->create(); $this->collection->addAttributeToSelect('*'); $this->customerRepository = $customerRepository; - $this->eavValidationRules = $eavValidationRules; $this->allowToShowHiddenAttributes = $allowToShowHiddenAttributes; $this->context = $context; - $this->fileProcessorFactory = $fileProcessorFactory; - $this->countryWithWebsiteSource = $countryWithWebsites; - $this->shareConfig = $shareConfig; + $this->fileUploaderDataResolver = $fileUploaderDataResolver; + $this->attributeMetadataResolver = $attributeMetadataResolver; $this->meta['general']['children'] = $this->getAttributesMeta( $eavConfig->getEntityType('customer_address') ); @@ -193,7 +120,7 @@ public function __construct( * @throws \Magento\Framework\Exception\LocalizedException * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function getData() + public function getData(): array { if (null !== $this->loadedData) { return $this->loadedData; @@ -210,7 +137,8 @@ public function getData() $defaultBilling = $customer->getDefaultBilling(); $defaultShipping = $customer->getDefaultShipping(); $this->prepareAddressData($addressId, $this->loadedData, $defaultBilling, $defaultShipping); - $this->overrideFileUploaderData($item, $this->loadedData[$addressId]); + + $this->fileUploaderDataResolver->overrideFileUploaderData($item, $this->loadedData[$addressId]); } if (null === $this->loadedData) { @@ -229,15 +157,15 @@ public function getData() * @param string|null $defaultShipping * @return void */ - private function prepareAddressData($addressId, array &$addresses, $defaultBilling, $defaultShipping) + private function prepareAddressData($addressId, array &$addresses, $defaultBilling, $defaultShipping): void { - if (null !== $defaultBilling && $addressId == $defaultBilling) { + if (null !== $defaultBilling && $addressId === $defaultBilling) { $addresses[$addressId]['default_billing'] = '1'; } - if (null !== $defaultShipping && $addressId == $defaultShipping) { + if (null !== $defaultShipping && $addressId === $defaultShipping) { $addresses[$addressId]['default_shipping'] = '1'; } - if (null !== $addresses[$addressId]['street'] && !is_array($addresses[$addressId]['street'])) { + if (null !== $addresses[$addressId]['street'] && !\is_array($addresses[$addressId]['street'])) { $addresses[$addressId]['street'] = explode("\n", $addresses[$addressId]['street']); } } @@ -249,7 +177,7 @@ private function prepareAddressData($addressId, array &$addresses, $defaultBilli * @throws \Magento\Framework\Exception\NoSuchEntityException * @return array */ - private function getDefaultData() + private function getDefaultData(): array { $parentId = $this->context->getRequestParam('parent_id'); $customer = $this->customerRepository->getById($parentId); @@ -262,30 +190,6 @@ private function getDefaultData() return $data; } - /** - * Override file uploader UI component data - * - * Overrides data for attributes with frontend_input equal to 'image' or 'file'. - * - * @param Address $entity - * @param array $entityData - * @return void - */ - private function overrideFileUploaderData($entity, array &$entityData) - { - $attributes = $entity->getAttributes(); - foreach ($attributes as $attribute) { - /** @var Attribute $attribute */ - if (in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { - $entityData[$attribute->getAttributeCode()] = $this->getFileUploaderData( - $entity->getEntityType(), - $attribute, - $entityData - ); - } - } - } - /** * Get attributes meta * @@ -299,259 +203,22 @@ protected function getAttributesMeta(Type $entityType): array $attributes = $entityType->getAttributeCollection(); /* @var AbstractAttribute $attribute */ foreach ($attributes as $attribute) { - $this->processFrontendInput($attribute, $meta); - - $code = $attribute->getAttributeCode(); - - if (in_array($attribute->getFrontendInput(), $this->bannedInputTypes)) { + if (\in_array($attribute->getFrontendInput(), $this->bannedInputTypes, true)) { continue; } - if (in_array($attribute->getAttributeCode(), $this->attributesToEliminate)) { + if (\in_array($attribute->getAttributeCode(), self::$attributesToEliminate, true)) { continue; } - // use getDataUsingMethod, since some getters are defined and apply additional processing of returning value - foreach ($this->metaProperties as $metaName => $origName) { - $value = $attribute->getDataUsingMethod($origName); - $meta[$code]['arguments']['data']['config'][$metaName] = ($metaName === 'label') ? __($value) : $value; - if ('frontend_input' === $origName) { - $meta[$code]['arguments']['data']['config']['formElement'] = $this->formElement[$value] ?? $value; - } - } - - if ($attribute->usesSource()) { - if ($code == AddressInterface::COUNTRY_ID) { - $meta[$code]['arguments']['data']['config']['options'] = $this->countryWithWebsiteSource - ->getAllOptions(); - } else { - $meta[$code]['arguments']['data']['config']['options'] = $attribute->getSource()->getAllOptions(); - } - } - - $rules = $this->eavValidationRules->build($attribute, $meta[$code]['arguments']['data']['config']); - if (!empty($rules)) { - $meta[$code]['arguments']['data']['config']['validation'] = $rules; - } - - $meta[$code]['arguments']['data']['config']['componentType'] = Field::NAME; - $meta[$code]['arguments']['data']['config']['visible'] = $this->canShowAttribute($attribute); - - $this->overrideFileUploaderMetadata($entityType, $attribute, $meta[$code]['arguments']['data']['config']); + $meta[$attribute->getAttributeCode()] = $this->attributeMetadataResolver->getAttributesMeta( + $attribute, + $entityType, + $this->allowToShowHiddenAttributes, + $this->getRequestFieldName() + ); } + $this->attributeMetadataResolver->processWebsiteMeta($meta); - $this->processWebsiteMeta($meta); return $meta; } - - /** - * Process attributes by frontend input type - * - * @param AttributeInterface $attribute - * @param array $meta - * @return void - */ - private function processFrontendInput(AttributeInterface $attribute, array &$meta) - { - $code = $attribute->getAttributeCode(); - if ($attribute->getFrontendInput() === 'boolean') { - $meta[$code]['arguments']['data']['config']['prefer'] = 'toggle'; - $meta[$code]['arguments']['data']['config']['valueMap'] = [ - 'true' => '1', - 'false' => '0', - ]; - } - } - - /** - * Detect can we show attribute on specific form or not - * - * @param AbstractAttribute $customerAttribute - * @return bool - */ - private function canShowAttribute(AbstractAttribute $customerAttribute): bool - { - $userDefined = (bool) $customerAttribute->getIsUserDefined(); - if (!$userDefined) { - return $customerAttribute->getIsVisible(); - } - - $canShowOnForm = $this->canShowAttributeInForm($customerAttribute); - - return ($this->allowToShowHiddenAttributes && $canShowOnForm) || - (!$this->allowToShowHiddenAttributes && $canShowOnForm && $customerAttribute->getIsVisible()); - } - - /** - * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... - * - * @param AbstractAttribute $customerAttribute - * @return bool - */ - private function canShowAttributeInForm(AbstractAttribute $customerAttribute): bool - { - $isRegistration = $this->context->getRequestParam($this->getRequestFieldName()) === null; - - if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { - return is_array($customerAttribute->getUsedInForms()) && - ( - (in_array('customer_account_create', $customerAttribute->getUsedInForms()) && $isRegistration) || - (in_array('customer_account_edit', $customerAttribute->getUsedInForms()) && !$isRegistration) - ); - } - return is_array($customerAttribute->getUsedInForms()) && - in_array('customer_address_edit', $customerAttribute->getUsedInForms()); - } - - /** - * Override file uploader UI component metadata - * - * Overrides metadata for attributes with frontend_input equal to 'image' or 'file'. - * - * @param Type $entityType - * @param AbstractAttribute $attribute - * @param array $config - * @return void - */ - private function overrideFileUploaderMetadata( - Type $entityType, - AbstractAttribute $attribute, - array &$config - ) { - if (in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { - $maxFileSize = self::MAX_FILE_SIZE; - - if (isset($config['validation']['max_file_size'])) { - $maxFileSize = (int)$config['validation']['max_file_size']; - } - - $allowedExtensions = []; - - if (isset($config['validation']['file_extensions'])) { - $allowedExtensions = explode(',', $config['validation']['file_extensions']); - array_walk($allowedExtensions, function (&$value) { - $value = strtolower(trim($value)); - }); - } - - $allowedExtensions = implode(' ', $allowedExtensions); - - $entityTypeCode = $entityType->getEntityTypeCode(); - $url = $this->getFileUploadUrl($entityTypeCode); - - $config = [ - 'formElement' => 'fileUploader', - 'componentType' => 'fileUploader', - 'maxFileSize' => $maxFileSize, - 'allowedExtensions' => $allowedExtensions, - 'uploaderConfig' => [ - 'url' => $url, - ], - 'label' => $this->getMetadataValue($config, 'label'), - 'sortOrder' => $this->getMetadataValue($config, 'sortOrder'), - 'required' => $this->getMetadataValue($config, 'required'), - 'visible' => $this->getMetadataValue($config, 'visible'), - 'validation' => $this->getMetadataValue($config, 'validation'), - ]; - } - } - - /** - * Add global scope parameter and filter options to website meta - * - * @param array $meta - * @return void - */ - private function processWebsiteMeta(&$meta) - { - if (isset($meta[CustomerInterface::WEBSITE_ID]) && $this->shareConfig->isGlobalScope()) { - $meta[CustomerInterface::WEBSITE_ID]['arguments']['data']['config']['isGlobalScope'] = 1; - } - - if (isset($meta[AddressInterface::COUNTRY_ID]) && !$this->shareConfig->isGlobalScope()) { - $meta[AddressInterface::COUNTRY_ID]['arguments']['data']['config']['filterBy'] = [ - 'target' => 'customer_form.customer_form_data_source:data.customer.website_id', - 'field' => 'website_ids' - ]; - } - } - - /** - * Retrieve metadata value - * - * @param array $config - * @param string $name - * @param mixed $default - * @return mixed - */ - private function getMetadataValue($config, $name, $default = null) - { - return $config[$name] ?? $default; - } - - /** - * Retrieve URL to file upload - * - * @param string $entityTypeCode - * @return string - */ - private function getFileUploadUrl($entityTypeCode): string - { - switch ($entityTypeCode) { - case CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER: - $url = 'customer/file/customer_upload'; - break; - - case AddressMetadataInterface::ENTITY_TYPE_ADDRESS: - $url = 'customer/file/address_upload'; - break; - - default: - $url = ''; - break; - } - return $url; - } - - /** - * Retrieve array of values required by file uploader UI component - * - * @param Type $entityType - * @param Attribute $attribute - * @param array $customerData - * @return array - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - private function getFileUploaderData( - Type $entityType, - Attribute $attribute, - array $customerData - ): array { - $attributeCode = $attribute->getAttributeCode(); - - $file = $customerData[$attributeCode] ?? ''; - - /** @var FileProcessor $fileProcessor */ - $fileProcessor = $this->fileProcessorFactory->create([ - 'entityTypeCode' => $entityType->getEntityTypeCode(), - ]); - - if (!empty($file) - && $fileProcessor->isExist($file) - ) { - $stat = $fileProcessor->getStat($file); - $viewUrl = $fileProcessor->getViewUrl($file, $attribute->getFrontendInput()); - - return [ - [ - 'file' => $file, - 'size' => null !== $stat ? $stat['size'] : 0, - 'url' => $viewUrl ?? '', - 'name' => basename($file), - 'type' => $fileProcessor->getMimeType($file), - ], - ]; - } - - return []; - } } diff --git a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php new file mode 100644 index 0000000000000..36ffcb2f5495b --- /dev/null +++ b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php @@ -0,0 +1,238 @@ +<?php +declare(strict_types=1); +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Model; + +use Magento\Customer\Model\ResourceModel\Address\Attribute\Source\CountryWithWebsites; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Ui\DataProvider\EavValidationRules; +use Magento\Ui\Component\Form\Field; +use Magento\Eav\Model\Entity\Type; +use Magento\Eav\Api\Data\AttributeInterface; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\Config\Share as ShareConfig; + +/** + * Class to build meta data of the customer or customer address attribute + */ +class AttributeMetadataResolver +{ + /** + * EAV attribute properties to fetch from meta storage + * @var array + */ + private static $metaProperties = [ + 'dataType' => 'frontend_input', + 'visible' => 'is_visible', + 'required' => 'is_required', + 'label' => 'frontend_label', + 'sortOrder' => 'sort_order', + 'notice' => 'note', + 'default' => 'default_value', + 'size' => 'multiline_count', + ]; + + /** + * Form element mapping + * + * @var array + */ + private static $formElement = [ + 'text' => 'input', + 'hidden' => 'input', + 'boolean' => 'checkbox', + ]; + + /** + * @var CountryWithWebsites + */ + private $countryWithWebsiteSource; + + /** + * @var EavValidationRules + */ + private $eavValidationRules; + + /** + * @var FileUploaderDataResolver + */ + private $fileUploaderDataResolver; + + /** + * @var ContextInterface + */ + private $context; + + /** + * @var ShareConfig + */ + private $shareConfig; + + /** + * @param CountryWithWebsites $countryWithWebsiteSource + * @param EavValidationRules $eavValidationRules + * @param \Magento\Customer\Model\FileUploaderDataResolver $fileUploaderDataResolver + * @param ContextInterface $context + * @param ShareConfig $shareConfig + */ + public function __construct( + CountryWithWebsites $countryWithWebsiteSource, + EavValidationRules $eavValidationRules, + fileUploaderDataResolver $fileUploaderDataResolver, + ContextInterface $context, + ShareConfig $shareConfig + ) { + $this->countryWithWebsiteSource = $countryWithWebsiteSource; + $this->eavValidationRules = $eavValidationRules; + $this->fileUploaderDataResolver = $fileUploaderDataResolver; + $this->context = $context; + $this->shareConfig = $shareConfig; + } + + /** + * Get meta data of the customer or customer address attribute + * + * @param AbstractAttribute $attribute + * @param Type $entityType + * @param bool $allowToShowHiddenAttributes + * @param string $requestFieldName + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function getAttributesMeta( + AbstractAttribute $attribute, + Type $entityType, + bool $allowToShowHiddenAttributes, + string $requestFieldName + ): array { + $meta = $this->modifyBooleanAttributeMeta($attribute); + // use getDataUsingMethod, since some getters are defined and apply additional processing of returning value + foreach (self::$metaProperties as $metaName => $origName) { + $value = $attribute->getDataUsingMethod($origName); + $meta['arguments']['data']['config'][$metaName] = ($metaName === 'label') ? __($value) : $value; + if ('frontend_input' === $origName) { + $meta['arguments']['data']['config']['formElement'] = self::$formElement[$value] ?? $value; + } + } + + if ($attribute->usesSource()) { + if ($attribute->getAttributeCode() === AddressInterface::COUNTRY_ID) { + $meta['arguments']['data']['config']['options'] = $this->countryWithWebsiteSource + ->getAllOptions(); + } else { + $meta['arguments']['data']['config']['options'] = $attribute->getSource()->getAllOptions(); + } + } + + $rules = $this->eavValidationRules->build($attribute, $meta['arguments']['data']['config']); + if (!empty($rules)) { + $meta['arguments']['data']['config']['validation'] = $rules; + } + + $meta['arguments']['data']['config']['componentType'] = Field::NAME; + $meta['arguments']['data']['config']['visible'] = $this->canShowAttribute( + $attribute, + $requestFieldName, + $allowToShowHiddenAttributes + ); + + $this->fileUploaderDataResolver->overrideFileUploaderMetadata( + $entityType, + $attribute, + $meta['arguments']['data']['config'] + ); + + return $meta; + } + + /** + * Detect can we show attribute on specific form or not + * + * @param AbstractAttribute $customerAttribute + * @param string $requestFieldName + * @param bool $allowToShowHiddenAttributes + * @return bool + */ + private function canShowAttribute( + AbstractAttribute $customerAttribute, + string $requestFieldName, + bool $allowToShowHiddenAttributes + ) { + $userDefined = (bool)$customerAttribute->getIsUserDefined(); + if (!$userDefined) { + return $customerAttribute->getIsVisible(); + } + + $canShowOnForm = $this->canShowAttributeInForm($customerAttribute, $requestFieldName); + + return ($allowToShowHiddenAttributes && $canShowOnForm) || + (!$allowToShowHiddenAttributes && $canShowOnForm && $customerAttribute->getIsVisible()); + } + + /** + * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... + * + * @param AbstractAttribute $customerAttribute + * @param string $requestFieldName + * @return bool + */ + private function canShowAttributeInForm(AbstractAttribute $customerAttribute, string $requestFieldName): bool + { + $isRegistration = $this->context->getRequestParam($requestFieldName) === null; + + if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { + return \is_array($customerAttribute->getUsedInForms()) && + ( + (\in_array('customer_account_create', $customerAttribute->getUsedInForms(), true) && $isRegistration) || + (\in_array('customer_account_edit', $customerAttribute->getUsedInForms(), true) && !$isRegistration) + ); + } + return \is_array($customerAttribute->getUsedInForms()) && + \in_array('customer_address_edit', $customerAttribute->getUsedInForms(), true); + } + + /** + * Modify boolean attribute meta data + * + * @param AttributeInterface $attribute + * @return array + */ + private function modifyBooleanAttributeMeta(AttributeInterface $attribute): array + { + $meta = []; + if ($attribute->getFrontendInput() === 'boolean') { + $meta['arguments']['data']['config']['prefer'] = 'toggle'; + $meta['arguments']['data']['config']['valueMap'] = [ + 'true' => '1', + 'false' => '0', + ]; + } + + return $meta; + } + + /** + * Add global scope parameter and filter options to website meta + * + * @param array $meta + * @return void + */ + public function processWebsiteMeta(&$meta): void + { + if (isset($meta[CustomerInterface::WEBSITE_ID]) && $this->shareConfig->isGlobalScope()) { + $meta[CustomerInterface::WEBSITE_ID]['arguments']['data']['config']['isGlobalScope'] = 1; + } + + if (isset($meta[AddressInterface::COUNTRY_ID]) && !$this->shareConfig->isGlobalScope()) { + $meta[AddressInterface::COUNTRY_ID]['arguments']['data']['config']['filterBy'] = [ + 'target' => 'customer_form.customer_form_data_source:data.customer.website_id', + 'field' => 'website_ids' + ]; + } + } +} diff --git a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php index 7ba6484dc3f2b..9131fb2ccdce2 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php +++ b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php @@ -6,112 +6,37 @@ */ namespace Magento\Customer\Model\Customer; -use Magento\Customer\Api\AddressMetadataInterface; -use Magento\Customer\Api\CustomerMetadataInterface; -use Magento\Customer\Api\Data\AddressInterface; -use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Model\Address; -use Magento\Customer\Model\Attribute; use Magento\Customer\Model\Customer; -use Magento\Customer\Model\FileProcessor; -use Magento\Customer\Model\FileProcessorFactory; -use Magento\Customer\Model\ResourceModel\Address\Attribute\Source\CountryWithWebsites; use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory as CustomerCollectionFactory; -use Magento\Eav\Api\Data\AttributeInterface; use Magento\Eav\Model\Config; use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; use Magento\Eav\Model\Entity\Type; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Session\SessionManagerInterface; -use Magento\Framework\View\Element\UiComponent\ContextInterface; -use Magento\Ui\Component\Form\Field; -use Magento\Ui\DataProvider\EavValidationRules; +use Magento\Customer\Model\FileUploaderDataResolver; +use Magento\Customer\Model\AttributeMetadataResolver; /** * Refactored version of Magento\Customer\Model\Customer\DataProvider with eliminated usage of addresses collection. - * - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DataProviderWithDefaultAddresses extends \Magento\Ui\DataProvider\AbstractDataProvider { - /** - * Maximum file size allowed for file_uploader UI component - */ - const MAX_FILE_SIZE = 2097152; - /** * @var array */ private $loadedData; - /** - * @var CountryWithWebsites - */ - private $countryWithWebsiteSource; - - /** - * @var \Magento\Customer\Model\Config\Share - */ - private $shareConfig; - - /** - * EAV attribute properties to fetch from meta storage - * @var array - */ - private $metaProperties = [ - 'dataType' => 'frontend_input', - 'visible' => 'is_visible', - 'required' => 'is_required', - 'label' => 'frontend_label', - 'sortOrder' => 'sort_order', - 'notice' => 'note', - 'default' => 'default_value', - 'size' => 'multiline_count', - ]; - - /** - * Form element mapping - * - * @var array - */ - private $formElement = [ - 'text' => 'input', - 'hidden' => 'input', - 'boolean' => 'checkbox', - ]; - - /** - * @var EavValidationRules - */ - private $eavValidationRules; - /** * @var SessionManagerInterface - * @since 100.1.0 */ private $session; - /** - * @var FileProcessorFactory - */ - private $fileProcessorFactory; - - /** - * File types allowed for file_uploader UI component - * - * @var array - */ - private $fileUploaderTypes = [ - 'image', - 'file', - ]; - /** * Customer fields that must be removed * * @var array */ - private $forbiddenCustomerFields = [ + private static $forbiddenCustomerFields = [ 'password_hash', 'rp_token', 'confirmation', @@ -135,54 +60,52 @@ class DataProviderWithDefaultAddresses extends \Magento\Ui\DataProvider\Abstract private $countryFactory; /** - * DataProviderWithDefaultAddresses constructor. - * + * @var FileUploaderDataResolver + */ + private $fileUploaderDataResolver; + + /** + * @var AttributeMetadataResolver + */ + private $attributeMetadataResolver; + + /** * @param string $name * @param string $primaryFieldName * @param string $requestFieldName - * @param EavValidationRules $eavValidationRules * @param CustomerCollectionFactory $customerCollectionFactory * @param Config $eavConfig - * @param FileProcessorFactory $fileProcessorFactory - * @param ContextInterface $context * @param \Magento\Directory\Model\CountryFactory $countryFactory * @param SessionManagerInterface $session - * @param \Magento\Customer\Model\Config\Share $shareConfig - * @param CountryWithWebsites $countryWithWebsites + * @param FileUploaderDataResolver $fileUploaderDataResolver + * @param AttributeMetadataResolver $attributeMetadataResolver * @param bool $allowToShowHiddenAttributes * @param array $meta * @param array $data - * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @throws \Magento\Framework\Exception\LocalizedException */ public function __construct( string $name, string $primaryFieldName, string $requestFieldName, - EavValidationRules $eavValidationRules, CustomerCollectionFactory $customerCollectionFactory, Config $eavConfig, - FileProcessorFactory $fileProcessorFactory, - ContextInterface $context, \Magento\Directory\Model\CountryFactory $countryFactory, - \Magento\Framework\Session\SessionManagerInterface $session, - \Magento\Customer\Model\Config\Share $shareConfig, - CountryWithWebsites $countryWithWebsites, + SessionManagerInterface $session, + FileUploaderDataResolver $fileUploaderDataResolver, + AttributeMetadataResolver $attributeMetadataResolver, $allowToShowHiddenAttributes = true, array $meta = [], array $data = [] ) { parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); - $this->eavValidationRules = $eavValidationRules; $this->collection = $customerCollectionFactory->create(); $this->collection->addAttributeToSelect('*'); - $this->fileProcessorFactory = $fileProcessorFactory; - $this->context = $context ?: ObjectManager::getInstance()->get(ContextInterface::class); $this->allowToShowHiddenAttributes = $allowToShowHiddenAttributes; $this->session = $session; - $this->countryWithWebsiteSource = $countryWithWebsites; - $this->shareConfig = $shareConfig; $this->countryFactory = $countryFactory; + $this->fileUploaderDataResolver = $fileUploaderDataResolver; + $this->attributeMetadataResolver = $attributeMetadataResolver; $this->meta['customer']['children'] = $this->getAttributesMeta( $eavConfig->getEntityType('customer') ); @@ -193,7 +116,7 @@ public function __construct( * * @return array */ - public function getData() + public function getData(): array { if (null !== $this->loadedData) { return $this->loadedData; @@ -203,11 +126,11 @@ public function getData() foreach ($items as $customer) { $result['customer'] = $customer->getData(); - $this->overrideFileUploaderData($customer, $result['customer']); + $this->fileUploaderDataResolver->overrideFileUploaderData($customer, $result['customer']); $result['customer'] = array_diff_key( $result['customer'], - array_flip($this->forbiddenCustomerFields) + array_flip(self::$forbiddenCustomerFields) ); unset($result['address']); @@ -254,73 +177,6 @@ private function prepareDefaultAddress($address): array return $addressData; } - /** - * Override file uploader UI component data - * - * Overrides data for attributes with frontend_input equal to 'image' or 'file'. - * - * @param Customer|Address $entity - * @param array $entityData - * @return void - */ - private function overrideFileUploaderData($entity, array &$entityData) - { - $attributes = $entity->getAttributes(); - foreach ($attributes as $attribute) { - /** @var Attribute $attribute */ - if (\in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { - $entityData[$attribute->getAttributeCode()] = $this->getFileUploaderData( - $entity->getEntityType(), - $attribute, - $entityData - ); - } - } - } - - /** - * Retrieve array of values required by file uploader UI component - * - * @param Type $entityType - * @param Attribute $attribute - * @param array $customerData - * @return array - * @SuppressWarnings(PHPMD.NPathComplexity) - */ - private function getFileUploaderData( - Type $entityType, - Attribute $attribute, - array $customerData - ): array { - $attributeCode = $attribute->getAttributeCode(); - - $file = $customerData[$attributeCode] ?? ''; - - /** @var FileProcessor $fileProcessor */ - $fileProcessor = $this->fileProcessorFactory->create([ - 'entityTypeCode' => $entityType->getEntityTypeCode(), - ]); - - if (!empty($file) - && $fileProcessor->isExist($file) - ) { - $stat = $fileProcessor->getStat($file); - $viewUrl = $fileProcessor->getViewUrl($file, $attribute->getFrontendInput()); - - return [ - [ - 'file' => $file, - 'size' => null !== $stat ? $stat['size'] : 0, - 'url' => $viewUrl ?? '', - 'name' => basename($file), - 'type' => $fileProcessor->getMimeType($file), - ], - ]; - } - - return []; - } - /** * Get attributes meta * @@ -334,209 +190,15 @@ protected function getAttributesMeta(Type $entityType): array $attributes = $entityType->getAttributeCollection(); /* @var AbstractAttribute $attribute */ foreach ($attributes as $attribute) { - $this->processFrontendInput($attribute, $meta); - - $code = $attribute->getAttributeCode(); - - // use getDataUsingMethod, since some getters are defined and apply additional processing of returning value - foreach ($this->metaProperties as $metaName => $origName) { - $value = $attribute->getDataUsingMethod($origName); - $meta[$code]['arguments']['data']['config'][$metaName] = ($metaName === 'label') ? __($value) : $value; - if ('frontend_input' === $origName) { - $meta[$code]['arguments']['data']['config']['formElement'] = $this->formElement[$value] ?? $value; - } - } - - if ($attribute->usesSource()) { - if ($code == AddressInterface::COUNTRY_ID) { - $meta[$code]['arguments']['data']['config']['options'] = $this->countryWithWebsiteSource - ->getAllOptions(); - } else { - $meta[$code]['arguments']['data']['config']['options'] = $attribute->getSource()->getAllOptions(); - } - } - - $rules = $this->eavValidationRules->build($attribute, $meta[$code]['arguments']['data']['config']); - if (!empty($rules)) { - $meta[$code]['arguments']['data']['config']['validation'] = $rules; - } - - $meta[$code]['arguments']['data']['config']['componentType'] = Field::NAME; - $meta[$code]['arguments']['data']['config']['visible'] = $this->canShowAttribute($attribute); - - $this->overrideFileUploaderMetadata($entityType, $attribute, $meta[$code]['arguments']['data']['config']); + $meta[$attribute->getAttributeCode()] = $this->attributeMetadataResolver->getAttributesMeta( + $attribute, + $entityType, + $this->allowToShowHiddenAttributes, + $this->getRequestFieldName() + ); } + $this->attributeMetadataResolver->processWebsiteMeta($meta); - $this->processWebsiteMeta($meta); return $meta; } - - /** - * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... - * - * @param AbstractAttribute $customerAttribute - * @return bool - */ - private function canShowAttributeInForm(AbstractAttribute $customerAttribute) - { - $isRegistration = $this->context->getRequestParam($this->getRequestFieldName()) === null; - - if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { - return \is_array($customerAttribute->getUsedInForms()) && - ( - (\in_array('customer_account_create', $customerAttribute->getUsedInForms()) && $isRegistration) || - (\in_array('customer_account_edit', $customerAttribute->getUsedInForms()) && !$isRegistration) - ); - } - return \is_array($customerAttribute->getUsedInForms()) && - \in_array('customer_address_edit', $customerAttribute->getUsedInForms()); - } - - /** - * Detect can we show attribute on specific form or not - * - * @param AbstractAttribute $customerAttribute - * @return bool - */ - private function canShowAttribute(AbstractAttribute $customerAttribute) - { - $userDefined = (bool) $customerAttribute->getIsUserDefined(); - if (!$userDefined) { - return $customerAttribute->getIsVisible(); - } - - $canShowOnForm = $this->canShowAttributeInForm($customerAttribute); - - return ($this->allowToShowHiddenAttributes && $canShowOnForm) || - (!$this->allowToShowHiddenAttributes && $canShowOnForm && $customerAttribute->getIsVisible()); - } - - /** - * Add global scope parameter and filter options to website meta - * - * @param array $meta - * @return void - */ - private function processWebsiteMeta(&$meta) - { - if (isset($meta[CustomerInterface::WEBSITE_ID]) && $this->shareConfig->isGlobalScope()) { - $meta[CustomerInterface::WEBSITE_ID]['arguments']['data']['config']['isGlobalScope'] = 1; - } - - if (isset($meta[AddressInterface::COUNTRY_ID]) && !$this->shareConfig->isGlobalScope()) { - $meta[AddressInterface::COUNTRY_ID]['arguments']['data']['config']['filterBy'] = [ - 'target' => '${ $.provider }:data.customer.website_id', - 'field' => 'website_ids' - ]; - } - } - - /** - * Override file uploader UI component metadata - * - * Overrides metadata for attributes with frontend_input equal to 'image' or 'file'. - * - * @param Type $entityType - * @param AbstractAttribute $attribute - * @param array $config - * @return void - */ - private function overrideFileUploaderMetadata( - Type $entityType, - AbstractAttribute $attribute, - array &$config - ) { - if (\in_array($attribute->getFrontendInput(), $this->fileUploaderTypes)) { - $maxFileSize = self::MAX_FILE_SIZE; - - if (isset($config['validation']['max_file_size'])) { - $maxFileSize = (int)$config['validation']['max_file_size']; - } - - $allowedExtensions = []; - - if (isset($config['validation']['file_extensions'])) { - $allowedExtensions = explode(',', $config['validation']['file_extensions']); - array_walk($allowedExtensions, function (&$value) { - $value = strtolower(trim($value)); - }); - } - - $allowedExtensions = implode(' ', $allowedExtensions); - - $entityTypeCode = $entityType->getEntityTypeCode(); - $url = $this->getFileUploadUrl($entityTypeCode); - - $config = [ - 'formElement' => 'fileUploader', - 'componentType' => 'fileUploader', - 'maxFileSize' => $maxFileSize, - 'allowedExtensions' => $allowedExtensions, - 'uploaderConfig' => [ - 'url' => $url, - ], - 'label' => $this->getMetadataValue($config, 'label'), - 'sortOrder' => $this->getMetadataValue($config, 'sortOrder'), - 'required' => $this->getMetadataValue($config, 'required'), - 'visible' => $this->getMetadataValue($config, 'visible'), - 'validation' => $this->getMetadataValue($config, 'validation'), - ]; - } - } - - /** - * Retrieve metadata value - * - * @param array $config - * @param string $name - * @param mixed $default - * @return mixed - */ - private function getMetadataValue($config, $name, $default = null) - { - return $config[$name] ?? $default; - } - - /** - * Retrieve URL to file upload - * - * @param string $entityTypeCode - * @return string - */ - private function getFileUploadUrl($entityTypeCode): string - { - switch ($entityTypeCode) { - case CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER: - $url = 'customer/file/customer_upload'; - break; - - case AddressMetadataInterface::ENTITY_TYPE_ADDRESS: - $url = 'customer/file/address_upload'; - break; - - default: - $url = ''; - break; - } - return $url; - } - - /** - * Process attributes by frontend input type - * - * @param AttributeInterface $attribute - * @param array $meta - * @return void - */ - private function processFrontendInput(AttributeInterface $attribute, array &$meta) - { - $code = $attribute->getAttributeCode(); - if ($attribute->getFrontendInput() === 'boolean') { - $meta[$code]['arguments']['data']['config']['prefer'] = 'toggle'; - $meta[$code]['arguments']['data']['config']['valueMap'] = [ - 'true' => '1', - 'false' => '0', - ]; - } - } } diff --git a/app/code/Magento/Customer/Model/FileUploaderDataResolver.php b/app/code/Magento/Customer/Model/FileUploaderDataResolver.php new file mode 100644 index 0000000000000..a132507deaa2f --- /dev/null +++ b/app/code/Magento/Customer/Model/FileUploaderDataResolver.php @@ -0,0 +1,201 @@ +<?php +declare(strict_types=1); +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Model; + +use Magento\Eav\Model\Entity\Type; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Customer\Api\AddressMetadataInterface; +use Magento\Customer\Api\CustomerMetadataInterface; + +/** + * Class to retrieve file uploader data for customer and customer address file & image attributes + */ +class FileUploaderDataResolver +{ + /** + * Maximum file size allowed for file_uploader UI component + */ + private const MAX_FILE_SIZE = 2097152; + + /** + * @var FileProcessorFactory + */ + private $fileProcessorFactory; + + /** + * File types allowed for file_uploader UI component + * + * @var array + */ + private $fileUploaderTypes = [ + 'image', + 'file', + ]; + + /** + * @param FileProcessorFactory $fileProcessorFactory + */ + public function __construct(FileProcessorFactory $fileProcessorFactory) { + $this->fileProcessorFactory = $fileProcessorFactory; + } + + /** + * Override file uploader UI component data + * + * Overrides data for attributes with frontend_input equal to 'image' or 'file'. + * + * @param Customer|Address $entity + * @param array $entityData + * @return void + */ + public function overrideFileUploaderData($entity, array &$entityData): void + { + $attributes = $entity->getAttributes(); + foreach ($attributes as $attribute) { + /** @var Attribute $attribute */ + if (\in_array($attribute->getFrontendInput(), $this->fileUploaderTypes, true)) { + $entityData[$attribute->getAttributeCode()] = $this->getFileUploaderData( + $entity->getEntityType(), + $attribute, + $entityData + ); + } + } + } + + /** + * Retrieve array of values required by file uploader UI component + * + * @param Type $entityType + * @param Attribute $attribute + * @param array $customerData + * @return array + */ + private function getFileUploaderData( + Type $entityType, + Attribute $attribute, + array $customerData + ): array { + $attributeCode = $attribute->getAttributeCode(); + + $file = $customerData[$attributeCode] ?? ''; + + /** @var FileProcessor $fileProcessor */ + $fileProcessor = $this->fileProcessorFactory->create([ + 'entityTypeCode' => $entityType->getEntityTypeCode(), + ]); + + if (!empty($file) + && $fileProcessor->isExist($file) + ) { + $stat = $fileProcessor->getStat($file); + $viewUrl = $fileProcessor->getViewUrl($file, $attribute->getFrontendInput()); + + return [ + [ + 'file' => $file, + 'size' => null !== $stat ? $stat['size'] : 0, + 'url' => $viewUrl ?? '', + 'name' => basename($file), + 'type' => $fileProcessor->getMimeType($file), + ], + ]; + } + + return []; + } + + /** + * Override file uploader UI component metadata + * + * Overrides metadata for attributes with frontend_input equal to 'image' or 'file'. + * + * @param Type $entityType + * @param AbstractAttribute $attribute + * @param array $config + * @return void + */ + public function overrideFileUploaderMetadata( + Type $entityType, + AbstractAttribute $attribute, + array &$config + ): void { + if (\in_array($attribute->getFrontendInput(), $this->fileUploaderTypes, true)) { + $maxFileSize = self::MAX_FILE_SIZE; + + if (isset($config['validation']['max_file_size'])) { + $maxFileSize = (int)$config['validation']['max_file_size']; + } + + $allowedExtensions = []; + + if (isset($config['validation']['file_extensions'])) { + $allowedExtensions = explode(',', $config['validation']['file_extensions']); + array_walk($allowedExtensions, function (&$value) { + $value = strtolower(trim($value)); + }); + } + + $allowedExtensions = implode(' ', $allowedExtensions); + + $entityTypeCode = $entityType->getEntityTypeCode(); + $url = $this->getFileUploadUrl($entityTypeCode); + + $config = [ + 'formElement' => 'fileUploader', + 'componentType' => 'fileUploader', + 'maxFileSize' => $maxFileSize, + 'allowedExtensions' => $allowedExtensions, + 'uploaderConfig' => [ + 'url' => $url, + ], + 'label' => $this->getMetadataValue($config, 'label'), + 'sortOrder' => $this->getMetadataValue($config, 'sortOrder'), + 'required' => $this->getMetadataValue($config, 'required'), + 'visible' => $this->getMetadataValue($config, 'visible'), + 'validation' => $this->getMetadataValue($config, 'validation'), + ]; + } + } + + /** + * Retrieve metadata value + * + * @param array $config + * @param string $name + * @param mixed $default + * @return mixed + */ + private function getMetadataValue($config, $name, $default = null) + { + return $config[$name] ?? $default; + } + + /** + * Retrieve URL to file upload + * + * @param string $entityTypeCode + * @return string + */ + private function getFileUploadUrl($entityTypeCode): string + { + switch ($entityTypeCode) { + case CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER: + $url = 'customer/file/customer_upload'; + break; + + case AddressMetadataInterface::ENTITY_TYPE_ADDRESS: + $url = 'customer/file/address_upload'; + break; + + default: + $url = ''; + break; + } + return $url; + } +} diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index 9e432f9f10e0c..1c30d08879eed 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -60,7 +60,7 @@ <dataType>text</dataType> </settings> </field> - <field name="default_billing" sortOrder="1" formElement="checkbox"> + <field name="default_billing" sortOrder="10" formElement="checkbox"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="default" xsi:type="number">0</item> @@ -83,7 +83,7 @@ </checkbox> </formElements> </field> - <field name="default_shipping" sortOrder="0" formElement="checkbox"> + <field name="default_shipping" sortOrder="5" formElement="checkbox"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="default" xsi:type="number">0</item> diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address-block.js b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address-block.js deleted file mode 100644 index a715aae1ebd96..0000000000000 --- a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address-block.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'uiElement' -], function($, Component) { - 'use strict'; - - return Component.extend({ - defaults: { - template: 'Magento_Customer/default-address' - } - }); -}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/address/modal.js b/app/code/Magento/Customer/view/adminhtml/web/js/address/modal.js deleted file mode 100644 index 94701f4c1af99..0000000000000 --- a/app/code/Magento/Customer/view/adminhtml/web/js/address/modal.js +++ /dev/null @@ -1,203 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'jquery', - 'Magento_Ui/js/modal/modal-component', - 'uiRegistry', - 'underscore' -], function ($, Modal, registry, _) { - 'use strict'; - - return Modal.extend({ - defaults: { - modules: { - emailProvider: '${ $.emailProvider }' - } - }, - - /** - * Initializes component. - * - * @returns {Object} Chainable. - */ - initialize: function () { - this._super(); - - // console.log(this.name); - - return this; - }, - - /** - * Open modal. - */ - openModal: function (data) { - debugger; - if (data == null){ - // add - this.setTitle(this.options.title); - this._super(); - } else { - // edit - var addressId = data.uuid; - var address = { - 'city': 'city', - 'company': 'company', - 'country_id': 'country_id', - 'customer_id': 'customer_id', - 'created_at': 'created_at', - 'default_billing': 'default_billing', - 'default_shipping': 'default_shipping', - 'entity_id': 'entity_id', - 'fax': 'fax', - 'firstname': 'firstname', - 'increment_id': 'increment_id', - 'is_active': 'is_active', - 'lastname': 'lastname', - 'middlename': 'middlename', - 'parent_id': 'parent_id', - 'postcode': 'postcode', - 'prefix': 'prefix', - 'region': 'region', - 'region_id': 'region_id', - 'street': [0, 1], - 'suffix': 'suffix', - 'telephone': 'telephone', - 'updated_at': 'updated_at', - 'vat_id': 'vat_id', - 'vat_is_valid': 'vat_is_valid', - 'vat_request_date': 'vat_request_date', - 'vat_request_id': 'vat_request_id', - 'vat_request_success': 'vat_request_success' - }; - - var source = registry.get('customer_form.customer_form_data_source'); - var modal = 'data.address_listing.address_form.update_customer_address_form_modal'; - - _.each(address, function(value, key) { - if (key === 'default_billing' || key === 'default_shipping') { - var defaultValue = source.get('data.address.' + addressId + '.' + value); - // convert boolean to integer - var val = +defaultValue; - source.set(modal + '.' + key, val.toString()); - } else if (key === 'street' && _.isArray(address[key])) { - _.each(address[key], function(element, index) { - source.set(modal + '.' + key + '[' + index + ']', source.get('data.address.' + addressId + '.' + key + '.' + element)); - }); - } else { - source.set(modal + '.' + key, source.get('data.address.' + addressId + '.' + value)); - } - }); - - this.setTitle(this.options.title); - this._super(); - } - }, - - /** - * Close popup modal. - * @public - */ - closeModal: function () { - debugger; - this._clearData(); - this._super(); - }, - - /** - * Clear modal data. - * - * @private - */ - _clearData: function () { - debugger; - var address = { - 'city': '', - 'company': '', - 'country_id': '', - 'default_billing': "0", - 'default_shipping': "0", - 'entity_id': '', - 'firstname': '', - 'is_active': '', - 'lastname': '', - 'middlename': '', - 'postcode': '', - 'prefix': '', - 'region': '', - 'region_id': '', - 'street[0]': '', - 'street[1]': '', - 'suffix': '', - 'telephone': '', - 'vat_id': '' - }; - - var source = registry.get('customer_form.customer_form_data_source'); - var modal = 'data.address_listing.address_form.update_customer_address_form_modal'; - - _.each(address, function(value, key) { - source.set(modal + '.' + key, value); - }); - }, - - /** - * Open modal. - */ - save: function () { - debugger; - - var address = { - 'city': 'city', - 'company': 'company', - 'country_id': 'country_id', - 'customer_id': 'customer_id', - 'created_at': 'created_at', - 'default_billing': 'default_billing', - 'default_shipping': 'default_shipping', - 'entity_id': 'entity_id', - 'fax': 'fax', - 'firstname': 'firstname', - 'increment_id': 'increment_id', - 'is_active': 'is_active', - 'lastname': 'lastname', - 'middlename': 'middlename', - 'parent_id': 'parent_id', - 'postcode': 'postcode', - 'prefix': 'prefix', - 'region': 'region', - 'region_id': 'region_id', - 'street': ['street[0]', 'street[1]'], - 'suffix': 'region_id', - 'telephone': 'telephone', - 'updated_at': 'updated_at', - 'vat_id': 'vat_id', - 'vat_is_valid': 'vat_is_valid', - 'vat_request_date': 'vat_request_date', - 'vat_request_id': 'vat_request_id', - 'vat_request_success': 'vat_request_success' - }; - - var source = registry.get('customer_form.customer_form_data_source'); - var formData = source.get('data.address_listing.address_form.update_customer_address_form_modal'); - var entityId = formData.entity_id; - - $.ajax({ - url: this.options.url, - showLoader: true, - data: formData, - type: "POST", - dataType: 'json', - success: function(data) { - console.log('SUCCESS: ', data); - }, - error: function(data) { - console.log('ERROR: ', data); - } - }); - } - }); -}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js deleted file mode 100644 index edfb004654a9b..0000000000000 --- a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -define([ - 'Magento_Ui/js/form/components/insert-form' -], function (Insert) { - 'use strict'; - - return Insert.extend({ - // Should be refactored after form save.!!!!! - // defaults: { - // updateModalProvider: '${ $.parentName }', - // subTitlePrefix: $t('Belongs to '), - // switcherSelector: '.store-switcher', - // toRemove: [], - // // imports: { - // // removeResponseData: '${ $.removeResponseProvider }', - // // modalTitle: '${ $.modalTitleProvider }', - // // modalSubTitle: '${ $.modalSubTitleProvider }', - // // destroyClosedModalContents: '${ $.updateModalProvider }:state' - // // }, - // // listens: { - // // responseData: 'afterUpdate', - // // removeResponseData: 'afterRemove', - // // modalTitle: 'changeModalTitle', - // // modalSubTitle: 'changeModalSubTitle' - // // }, - // modules: { - // updateModal: '${ $.updateModalProvider }', - // removeModal: '${ $.removeModalProvider }', - // upcomingListing: 'index = ${ $.upcomingListingProvider }' - // } - // }, - // - // /** @inheritdoc **/ - // initialize: function () { - // _.bindAll(this, 'onSwitcherSelect'); - // this._super(); - // this.updateModal(this.initSwitcherHandler.bind(this)); - // - // return this; - // }, - // - // initConfig: function (options) { - // debugger; - // return this._super(); - // }, - // - // /** @inheritdoc */ - // destroyInserted: function () { - // if (this.isRendered) { - // _.each(this.toRemove, function (componentName) { - // registry.get(componentName, function (component) { - // if (component.hasOwnProperty('delegate')) { - // component.delegate('destroy'); - // } else { - // component.destroy(); - // } - // }); - // }); - // } - // - // this._super(); - // }, - // - // // /** - // // * Form save callback. - // // * - // // * @param {Object} data - // // */ - // // afterUpdate: function (data) { - // // if (!data.error) { - // // this.updateModal('closeModal'); - // // this.upcomingListing('reload'); - // // } - // // }, - // - // // /** - // // * Form remove callback. - // // * - // // * @param {Object} data - // // */ - // // afterRemove: function (data) { - // // if (!data.error) { - // // this.removeModal('closeModal'); - // // this.afterUpdate(data); - // // } - // // }, - // - // // /** - // // * Change modal title. - // // * - // // * @param {String} title - // // */ - // // changeModalTitle: function (title) { - // // this.updateModal('setTitle', title); - // // }, - // // - // // /** - // // * Change modal sub title. - // // * - // // * @param {String} subTitle - // // */ - // // changeModalSubTitle: function (subTitle) { - // // subTitle = subTitle ? - // // this.subTitlePrefix + this.modalTitle + ' ' + subTitle : - // // ''; - // // - // // this.updateModal('setSubTitle', subTitle); - // // }, - // - // /** - // * Destroy contents of modal when it is closed - // * - // * @param {Boolean} state - // */ - // destroyClosedModalContents: function (state) { - // if (state === false) { - // this.destroyInserted(); - // } - // }, - // - // /** - // * Switcher initialization. - // */ - // initSwitcherHandler: function () { - // var switcherSelector = this.updateModal().rootSelector + ' ' + this.switcherSelector, - // self = this; - // - // $.async(switcherSelector, function (switcher) { - // $(switcher).on('click', 'li a', self.onSwitcherSelect); - // }); - // }, - // - // /** - // * Store switcher selection handler. - // * @param {Object} e - event object. - // */ - // onSwitcherSelect: function (e) { - // var self = this, - // param = $(e.currentTarget).data('param'), - // params = { - // store: 0 - // }; - // - // params[param] = $(e.currentTarget).data('value'); - // - // uiConfirm({ - // content: $t('Please confirm scope switching. All data that hasn\'t been saved will be lost.'), - // actions: { - // - // /** Confirm callback. */ - // confirm: function () { - // self.destroyInserted(); - // params = _.extend(self.previousParams, params); - // self.render(params); - // } - // } - // }); - // } - }); -}); diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index 1eb3652628c28..6fc8fcac6099f 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -351,7 +351,7 @@ </argument> <settings> <componentType>button</componentType> - <title>Edit + Edit true ${ $.provider}:data.default_billing_address.entity_id @@ -398,7 +398,7 @@ button - Edit + Edit true ${ $.provider}:data.default_shipping_address.entity_id @@ -428,7 +428,7 @@ - Add New Address + Add New Address ${ $.provider}:data.customer_id @@ -440,9 +440,8 @@ - + - customer_address_edit 1 diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/collection.js b/app/code/Magento/Ui/view/base/web/js/form/components/collection.js index dbea61b13e626..96d2a9e83a8f5 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/collection.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/collection.js @@ -5,9 +5,6 @@ /** * @api - * @deprecated as customer addresses are handled by ui components. - * This collection component manages rendering address list in Addresses tab of customer. - * Now address list is rendered with ui component listing. */ define([ 'underscore', @@ -49,7 +46,6 @@ define([ * @param {Object} elem - Incoming child. */ initElement: function (elem) { - debugger; this._super(); elem.activate(); diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js b/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js index ae7f375bdca02..045c25ab7911f 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/collection/item.js @@ -5,9 +5,6 @@ /** * @api - * @deprecated as customer addresses are handled by ui components. - * This item component renders address list item preview in Addresses tab. - * But now address list item is rendered with ui component in customer form. */ define([ 'underscore', diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt index 5c5c380c4dd33..877583e5b6a29 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/blacklist/strict_type.txt @@ -1 +1 @@ -# Format: or simply \ No newline at end of file +# Format: or simply diff --git a/setup/performance-toolkit/config/customerConfig.xml b/setup/performance-toolkit/config/customerConfig.xml index b77d9f6d4c941..8fd74d7b53885 100644 --- a/setup/performance-toolkit/config/customerConfig.xml +++ b/setup/performance-toolkit/config/customerConfig.xml @@ -6,5 +6,5 @@ */ --> - 5 + 2 From bfa9abe824752732e46b759e1b7c99be6c4f034b Mon Sep 17 00:00:00 2001 From: Oleksii Korshenko Date: Tue, 30 Oct 2018 12:57:08 -0500 Subject: [PATCH 082/704] magento/magento2#18840: Invalid Unit Test Annotations - fixed invalid unit tests annotations that assert exception messages --- dev/tests/unit/phpunit.xml.dist | 41 --------------------------------- 1 file changed, 41 deletions(-) diff --git a/dev/tests/unit/phpunit.xml.dist b/dev/tests/unit/phpunit.xml.dist index 3de27d9da88b8..a91e0003a196e 100644 --- a/dev/tests/unit/phpunit.xml.dist +++ b/dev/tests/unit/phpunit.xml.dist @@ -57,47 +57,6 @@ expectedExceptionMessageRegExp - - - expectedExceptionText - - - expectedMessage - - - message - - - exceptedExceptionMessage - - - exectedExceptionMessage - - - ExceptedExceptionMessage - - - expected - - - expecteExceptionMessage - - - true - - - assertException - - exceptionMessage - paran - - - - exceptionMessage - - - magentoAppArea - From 3478e785f5a08e6220ff604836cec9199c2f30ba Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Wed, 31 Oct 2018 17:15:41 +0200 Subject: [PATCH 083/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Fix static tests; --- .../Adminhtml/Edit/Address/CancelButton.php | 1 + .../Adminhtml/Edit/Address/SaveButton.php | 1 + .../Address/AbstractDefaultAddress.php | 4 +- .../Address/DefaultBillingAddress.php | 1 + .../Address/DefaultShippingAddress.php | 1 + .../Controller/Adminhtml/Address/Delete.php | 4 +- .../Adminhtml/Address/MassDelete.php | 10 +- .../Controller/Adminhtml/Address/Save.php | 4 +- .../Controller/Adminhtml/Address/Validate.php | 14 ++- .../Adminhtml/File/Address/Upload.php | 3 +- .../Customer/Model/Address/DataProvider.php | 1 + .../Model/AttributeMetadataResolver.php | 13 +- .../Customer/Model/Customer/DataProvider.php | 14 ++- .../DataProviderWithDefaultAddresses.php | 6 +- .../Model/FileUploaderDataResolver.php | 4 +- .../Model/ResourceModel/Address/Relation.php | 2 - .../Unit/Model/Address/DataProviderTest.php | 62 ++++------ .../ResourceModel/CustomerRepositoryTest.php | 6 - .../Ui/Component/Form/AddressFieldsetTest.php | 2 +- .../Listing/Address/Column/Countries.php | 6 +- .../ui_component/customer_address_form.xml | 2 +- .../web/js/address/default-address.js | 15 ++- app/code/Magento/Ui/Component/Layout/Tabs.php | 115 ++++++++++-------- .../web/css/source/_module.less | 15 +-- 24 files changed, 167 insertions(+), 139 deletions(-) diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php index 6270e8073d0e8..d94b956918370 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/CancelButton.php @@ -1,4 +1,5 @@ filter->getCollection($this->collectionFactory->create()); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php index 9113640112e3b..8b70263d3f95b 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php @@ -1,4 +1,5 @@ formFactory->create('customer_address', 'adminhtml_customer_address'); $formData = $addressForm->extractData($this->getRequest()); diff --git a/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php b/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php index be1b1aec7b3a3..afa62d2148eb4 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php @@ -10,6 +10,7 @@ use Magento\Customer\Api\AddressMetadataInterface; use Magento\Customer\Model\FileUploader; use Magento\Customer\Model\FileUploaderFactory; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Exception\LocalizedException; use Psr\Log\LoggerInterface; @@ -17,7 +18,7 @@ /** * Uploads files for customer address */ -class Upload extends Action +class Upload extends Action implements HttpGetActionInterface { /** * Authorization level of a basic admin session diff --git a/app/code/Magento/Customer/Model/Address/DataProvider.php b/app/code/Magento/Customer/Model/Address/DataProvider.php index 5fd7c884cf6b5..1b4bcf94fec15 100644 --- a/app/code/Magento/Customer/Model/Address/DataProvider.php +++ b/app/code/Magento/Customer/Model/Address/DataProvider.php @@ -85,6 +85,7 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider * @param array $data * @param bool $allowToShowHiddenAttributes * @throws \Magento\Framework\Exception\LocalizedException + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( $name, diff --git a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php index 36ffcb2f5495b..6b006d7f44e7e 100644 --- a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php +++ b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php @@ -19,6 +19,7 @@ /** * Class to build meta data of the customer or customer address attribute + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class AttributeMetadataResolver { @@ -188,8 +189,16 @@ private function canShowAttributeInForm(AbstractAttribute $customerAttribute, st if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { return \is_array($customerAttribute->getUsedInForms()) && ( - (\in_array('customer_account_create', $customerAttribute->getUsedInForms(), true) && $isRegistration) || - (\in_array('customer_account_edit', $customerAttribute->getUsedInForms(), true) && !$isRegistration) + (\in_array( + 'customer_account_create', + $customerAttribute->getUsedInForms(), + true + ) && $isRegistration) || + (\in_array( + 'customer_account_edit', + $customerAttribute->getUsedInForms(), + true + ) && !$isRegistration) ); } return \is_array($customerAttribute->getUsedInForms()) && diff --git a/app/code/Magento/Customer/Model/Customer/DataProvider.php b/app/code/Magento/Customer/Model/Customer/DataProvider.php index c834dd26824cd..7b770d20263dc 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProvider.php +++ b/app/code/Magento/Customer/Model/Customer/DataProvider.php @@ -29,6 +29,8 @@ use Magento\Ui\DataProvider\EavValidationRules; /** + * Data provider for customer and customer address data and meta data + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * * @deprecated \Magento\Customer\Model\Address\DataProvider is used instead @@ -148,18 +150,20 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider private $allowToShowHiddenAttributes; /** - * @param string $name - * @param string $primaryFieldName - * @param string $requestFieldName + * DataProvider constructor. + * @param $name + * @param $primaryFieldName + * @param $requestFieldName * @param EavValidationRules $eavValidationRules * @param CustomerCollectionFactory $customerCollectionFactory * @param Config $eavConfig * @param FilterPool $filterPool - * @param FileProcessorFactory $fileProcessorFactory - * @param ContextInterface $context + * @param FileProcessorFactory|null $fileProcessorFactory * @param array $meta * @param array $data + * @param ContextInterface|null $context * @param bool $allowToShowHiddenAttributes + * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( diff --git a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php index 9131fb2ccdce2..eafa5907efac9 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php +++ b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php @@ -42,11 +42,6 @@ class DataProviderWithDefaultAddresses extends \Magento\Ui\DataProvider\Abstract 'confirmation', ]; - /* - * @var ContextInterface - */ - private $context; - /** * Allow to manage attributes, even they are hidden on storefront * @@ -83,6 +78,7 @@ class DataProviderWithDefaultAddresses extends \Magento\Ui\DataProvider\Abstract * @param array $meta * @param array $data * @throws \Magento\Framework\Exception\LocalizedException + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( string $name, diff --git a/app/code/Magento/Customer/Model/FileUploaderDataResolver.php b/app/code/Magento/Customer/Model/FileUploaderDataResolver.php index a132507deaa2f..9dd5084739c74 100644 --- a/app/code/Magento/Customer/Model/FileUploaderDataResolver.php +++ b/app/code/Magento/Customer/Model/FileUploaderDataResolver.php @@ -39,7 +39,9 @@ class FileUploaderDataResolver /** * @param FileProcessorFactory $fileProcessorFactory */ - public function __construct(FileProcessorFactory $fileProcessorFactory) { + public function __construct( + FileProcessorFactory $fileProcessorFactory + ) { $this->fileProcessorFactory = $fileProcessorFactory; } diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php index cbfebe87812bc..37a633d47f512 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php @@ -46,7 +46,6 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) $changedAddresses['default_billing'] = $object->getId(); } elseif ($customer->getDefaultBillingAddress() && (int)$customer->getDefaultBillingAddress()->getId() === (int)$object->getId() - && !$object->getIsDefaultBilling() ) { $changedAddresses['default_billing'] = null; } @@ -55,7 +54,6 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) $changedAddresses['default_shipping'] = $object->getId(); } elseif ($customer->getDefaultShippingAddress() && (int)$customer->getDefaultShippingAddress()->getId() === (int)$object->getId() - && !$object->getIsDefaultShipping() ) { $changedAddresses['default_shipping'] = null; } diff --git a/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php index 815a47288e370..81266a8934c87 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php @@ -7,16 +7,15 @@ namespace Magento\Customer\Test\Unit\Model\Address; use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\AttributeMetadataResolver; +use Magento\Customer\Model\FileUploaderDataResolver; use Magento\Customer\Model\ResourceModel\Address\CollectionFactory; use Magento\Customer\Model\ResourceModel\Address\Collection as AddressCollection; use Magento\Eav\Model\Config; use Magento\Eav\Model\Entity\Type; -use Magento\Ui\DataProvider\EavValidationRules; -use Magento\Customer\Model\Attribute as AttributeModel; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Customer\Model\Address as AddressModel; -use Magento\Customer\Model\FileProcessorFactory; class DataProviderTest extends \PHPUnit\Framework\TestCase { @@ -45,26 +44,11 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase */ private $eavConfig; - /** - * @var EavValidationRules|\PHPUnit_Framework_MockObject_MockObject - */ - private $eavValidationRules; - /* * @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject */ private $context; - /** - * @var \Magento\Customer\Model\Config\Share|\PHPUnit_Framework_MockObject_MockObject - */ - private $shareConfig; - - /** - * @var FileProcessorFactory|\PHPUnit_Framework_MockObject_MockObject - */ - private $fileProcessorFactory; - /** * @var Type|\PHPUnit_Framework_MockObject_MockObject */ @@ -76,9 +60,14 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase private $address; /** - * @var AttributeModel|\PHPUnit_Framework_MockObject_MockObject + * @var FileUploaderDataResolver|\PHPUnit_Framework_MockObject_MockObject + */ + private $fileUploaderDataResolver; + + /** + * @var AttributeMetadataResolver|\PHPUnit_Framework_MockObject_MockObject */ - private $attribute; + private $attributeMetadataResolver; /** * @var \Magento\Customer\Model\Address\DataProvider @@ -88,6 +77,12 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase protected function setUp() { $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->fileUploaderDataResolver = $this->getMockBuilder(FileUploaderDataResolver::class) + ->disableOriginalConstructor() + ->getMock(); + $this->attributeMetadataResolver = $this->getMockBuilder(AttributeMetadataResolver::class) + ->disableOriginalConstructor() + ->getMock(); $this->addressCollectionFactory = $this->getMockBuilder(CollectionFactory::class) ->disableOriginalConstructor() ->setMethods(['create']) @@ -96,13 +91,7 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); $this->customerRepository = $this->getMockForAbstractClass(CustomerRepositoryInterface::class); - $this->eavValidationRules = $this->createMock(EavValidationRules::class); $this->context = $this->getMockForAbstractClass(ContextInterface::class); - $this->fileProcessorFactory = $this->getMockBuilder(FileProcessorFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - $this->shareConfig = $this->createMock(\Magento\Customer\Model\Config\Share::class); $this->addressCollectionFactory->expects($this->once()) ->method('create') ->willReturn($this->collection); @@ -122,9 +111,6 @@ protected function setUp() $this->address = $this->getMockBuilder(AddressModel::class) ->disableOriginalConstructor() ->getMock(); - $this->attribute = $this->getMockBuilder(AttributeModel::class) - ->disableOriginalConstructor() - ->getMock(); $this->model = $objectManagerHelper->getObject( \Magento\Customer\Model\Address\DataProvider::class, @@ -135,10 +121,9 @@ protected function setUp() 'addressCollectionFactory' => $this->addressCollectionFactory, 'customerRepository' => $this->customerRepository, 'eavConfig' => $this->eavConfig, - 'eavValidationRules' => $this->eavValidationRules, 'context' => $this->context, - 'fileProcessorFactory' => $this->fileProcessorFactory, - 'shareConfig' => $this->shareConfig, + 'fileUploaderDataResolver' => $this->fileUploaderDataResolver, + 'attributeMetadataResolver' => $this->attributeMetadataResolver, [], [], true @@ -146,7 +131,7 @@ protected function setUp() ); } - public function testGetDefaultData() + public function testGetDefaultData(): void { $expectedData = [ '' => [ @@ -176,7 +161,7 @@ public function testGetDefaultData() $this->assertEquals($expectedData, $this->model->getData()); } - public function testGetData() + public function testGetData(): void { $expectedData = [ '3' => [ @@ -221,12 +206,9 @@ public function testGetData() 'lastname' => 'Doe', 'street' => "42000 Ave W 55 Cedar City\nApt. 33" ]); - $this->address->expects($this->once()) - ->method('getAttributes') - ->willReturn([$this->attribute]); - $this->attribute->expects($this->once()) - ->method('getFrontendInput') - ->willReturn(null); + $this->fileUploaderDataResolver->expects($this->once()) + ->method('overrideFileUploaderData') + ->willReturnSelf(); $this->assertEquals($expectedData, $this->model->getData()); } diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php index 1d262e7549873..034832ce72bfb 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php @@ -377,12 +377,6 @@ public function testSaveWithPasswordHash() 'getFirstFailure', 'getLockExpires', ]); - $region = $this->getMockForAbstractClass( - \Magento\Customer\Api\Data\RegionInterface::class, - [], - '', - false - ); $origCustomer = $this->customer; $customerModel = $this->createPartialMock(\Magento\Customer\Model\Customer::class, [ diff --git a/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php b/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php index 82ce4249bcd85..31a8a1bfacc64 100644 --- a/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php +++ b/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -namespace Magento\Ui\Test\Unit\Component\Form; +namespace Magento\Customer\Test\Unit\Ui\Component\Form; use Magento\Customer\Ui\Component\Form\AddressFieldset; use Magento\Framework\View\Element\UiComponent\ContextInterface; diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php index 438a16ec3ba55..d05d5d1c592a7 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Countries.php @@ -1,5 +1,9 @@ text - + 0 diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js index 3a1500e5dc99e..b4ec734cb4033 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js @@ -9,16 +9,23 @@ define([ 'use strict'; return Button.extend({ + defaults: { + entity_id: null, + parent_id: null + }, + + /** + * Initializes component. + * + * @returns {Button} + */ initialize: function () { this._super(); if (!this.parent_id) { this.visible(this.entity_id); } - }, - defaults: { - entity_id: null, - parent_id: null + return this; }, /** diff --git a/app/code/Magento/Ui/Component/Layout/Tabs.php b/app/code/Magento/Ui/Component/Layout/Tabs.php index 02e8979f525ef..f5a9c86977c3c 100644 --- a/app/code/Magento/Ui/Component/Layout/Tabs.php +++ b/app/code/Magento/Ui/Component/Layout/Tabs.php @@ -5,10 +5,8 @@ */ namespace Magento\Ui\Component\Layout; -use Magento\Framework\View\Element\Template; use Magento\Framework\View\Element\UiComponent\BlockWrapperInterface; use Magento\Framework\View\Element\UiComponent\DataSourceInterface; -use Magento\Framework\View\Element\UiComponent\LayoutInterface; use Magento\Framework\View\Element\UiComponentFactory; use Magento\Framework\View\Element\UiComponentInterface; use Magento\Ui\Component\Form\Fieldset; @@ -17,7 +15,7 @@ /** * Class Tabs */ -class Tabs extends \Magento\Framework\View\Layout\Generic implements LayoutInterface +class Tabs extends \Magento\Framework\View\Layout\Generic { /** * @var string @@ -97,54 +95,8 @@ protected function addChildren(array &$topNode, UiComponentInterface $component, $name = $childComponent->getName(); $config = $childComponent->getData('config'); $collectedComponents[$name] = true; - if (isset($config['is_collection']) && $config['is_collection'] === true) { - $label = $childComponent->getData('config/label'); - $this->component->getContext()->addComponentDefinition( - 'collection', - [ - 'component' => 'Magento_Ui/js/form/components/collection', - 'extends' => $this->namespace - ] - ); - - /** - * @var UiComponentInterface $childComponent - * @var array $structure - */ - list($childComponent, $structure) = $this->prepareChildComponents($childComponent, $name); - - $childrenStructure = $structure[$name]['children']; - - $structure[$name]['children'] = [ - $name . '_collection' => [ - 'type' => 'collection', - 'config' => [ - 'active' => 1, - 'removeLabel' => __('Remove %1', $label), - 'addLabel' => __('Add New %1', $label), - 'removeMessage' => $childComponent->getData('config/removeMessage'), - 'itemTemplate' => 'item_template', - ], - 'children' => [ - 'item_template' => ['type' => $this->namespace, - 'isTemplate' => true, - 'component' => 'Magento_Ui/js/form/components/collection/item', - 'childType' => 'group', - 'config' => [ - 'label' => __('New %1', $label), - ], - 'children' => $childrenStructure - ] - ] - ] - ]; - } else { - /** - * @var UiComponentInterface $childComponent - * @var array $structure - */ - list($childComponent, $structure) = $this->prepareChildComponents($childComponent, $name); - } + + [$childComponent, $structure] = $this->buildChildComponentStructure($config, $childComponent); $tabComponent = $this->createTabComponent($childComponent, $name); @@ -172,6 +124,67 @@ protected function addChildren(array &$topNode, UiComponentInterface $component, $topNode = $this->structure; } + /** + * Build child components structure of the tab + * + * @param array $config + * @param UiComponentInterface $childComponent + * @return array + */ + private function buildChildComponentStructure(array $config, $childComponent): array + { + $name = $childComponent->getName(); + if (isset($config['is_collection']) && $config['is_collection'] === true) { + $label = $childComponent->getData('config/label'); + $this->component->getContext()->addComponentDefinition( + 'collection', + [ + 'component' => 'Magento_Ui/js/form/components/collection', + 'extends' => $this->namespace + ] + ); + /** + * @var UiComponentInterface $childComponent + * @var array $structure + */ + [$childComponent, $structure] = $this->prepareChildComponents($childComponent, $name); + + $childrenStructure = $structure[$name]['children']; + + $structure[$name]['children'] = [ + $name . '_collection' => [ + 'type' => 'collection', + 'config' => [ + 'active' => 1, + 'removeLabel' => __('Remove %1', $label), + 'addLabel' => __('Add New %1', $label), + 'removeMessage' => $childComponent->getData('config/removeMessage'), + 'itemTemplate' => 'item_template', + ], + 'children' => [ + 'item_template' => ['type' => $this->namespace, + 'isTemplate' => true, + 'component' => 'Magento_Ui/js/form/components/collection/item', + 'childType' => 'group', + 'config' => [ + 'label' => __('New %1', $label), + ], + 'children' => $childrenStructure + ] + ] + ] + ]; + } else { + /** + * @var UiComponentInterface $childComponent + * @var array $structure + */ + [$childComponent, $structure] = $this->prepareChildComponents($childComponent, $name); + } + + return [$childComponent, $structure]; + } + /** * Add wrapped layout block * diff --git a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less index 8a5fe698eb196..876859ff38d1a 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less @@ -3,7 +3,7 @@ // * See COPYING.txt for license details. // */ -.customer_form_areas_address_address_customer_address_update_modal_update_customer_address_form_loader { +.customer_form_areas_address_address_customer_address_update_modal_update_customer_address_form_loader { .admin__field { .admin__field { .admin__field-label { @@ -15,10 +15,12 @@ .customer-address-form { - *, *:before, *:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; + *, + *:before, + *:after { box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; } address { @@ -26,8 +28,7 @@ } .customer-default-billing-address-content, - .customer-default-shipping-address-content - { + .customer-default-shipping-address-content { float: left; width: 550px; } @@ -52,10 +53,10 @@ } .add-new-address-button { - position: relative; clear: both; float: right; margin-bottom: 30px; + position: relative; } .address-information { From 47251f6b9802a579fadd55146c649c1a2bf04140 Mon Sep 17 00:00:00 2001 From: Nathan Smith Date: Wed, 31 Oct 2018 10:24:09 -0500 Subject: [PATCH 084/704] MAGETWO-93181: Grouped product doesn't take care about his Linked Products when SalableQuantity < ProductLink.ExtensionAttributes.Qty after Source Deduction - Added the out of stock filter to the grouped product model to replicate frontend behavior --- .../Model/Product/Type/Grouped.php | 14 +++- .../Api/CartItemRepositoryTest.php | 68 +++++++++++++++++ ...oduct_grouped_with_simple_out_of_stock.php | 75 +++++++++++++++++++ ...uped_with_simple_out_of_stock_rollback.php | 34 +++++++++ 4 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/CartItemRepositoryTest.php create mode 100644 dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_simple_out_of_stock.php create mode 100644 dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_simple_out_of_stock_rollback.php diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php index 80824d45cb6e5..8844aedcb3d99 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php @@ -8,6 +8,8 @@ namespace Magento\GroupedProduct\Model\Product\Type; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\App\ObjectManager; +use Magento\CatalogInventory\Helper\Stock as StockHelper; /** * Grouped product type model @@ -86,6 +88,11 @@ class Grouped extends \Magento\Catalog\Model\Product\Type\AbstractType */ protected $msrpData; + /** + * @var \Magento\CatalogInventory\Helper\Stock|null + */ + private $stockHelper; + /** * @param \Magento\Catalog\Model\Product\Option $catalogProductOption * @param \Magento\Eav\Model\Config $eavConfig @@ -102,6 +109,7 @@ class Grouped extends \Magento\Catalog\Model\Product\Type\AbstractType * @param \Magento\Framework\App\State $appState * @param \Magento\Msrp\Helper\Data $msrpData * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer + * @param \Magento\CatalogInventory\Helper\Stock|null $stockHelper * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -119,13 +127,15 @@ public function __construct( \Magento\Catalog\Model\Product\Attribute\Source\Status $catalogProductStatus, \Magento\Framework\App\State $appState, \Magento\Msrp\Helper\Data $msrpData, - \Magento\Framework\Serialize\Serializer\Json $serializer = null + \Magento\Framework\Serialize\Serializer\Json $serializer = null, + StockHelper $stockHelper = null ) { $this->productLinks = $catalogProductLink; $this->_storeManager = $storeManager; $this->_catalogProductStatus = $catalogProductStatus; $this->_appState = $appState; $this->msrpData = $msrpData; + $this->stockHelper = $stockHelper ?: ObjectManager::getInstance()->get(StockHelper::class); parent::__construct( $catalogProductOption, $eavConfig, @@ -218,6 +228,8 @@ public function getAssociatedProducts($product) ['in' => $this->getStatusFilters($product)] ); + $this->stockHelper->addIsInStockFilterToCollection($collection); + foreach ($collection as $item) { $associatedProducts[] = $item; } diff --git a/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/CartItemRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/CartItemRepositoryTest.php new file mode 100644 index 0000000000000..61f07ac3aa043 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/CartItemRepositoryTest.php @@ -0,0 +1,68 @@ +objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + } + + /** + * @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php + * @magentoApiDataFixture Magento/GroupedProduct/_files/product_grouped_with_simple_out_of_stock.php + */ + public function testAddGroupedProductToCartThatHasAnOutOfStockItemInTheGroup() + { + $this->_markTestAsRestOnly(); + + /** @var \Magento\Catalog\Model\Product $product */ + $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class)->load('100000003'); + $productSku = $product->getSku(); + /** @var Quote $quote */ + $quote = $this->objectManager->create(Quote::class); + $quote->load('test_order_1', 'reserved_order_id'); + $cartId = $quote->getId(); + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . $cartId . '/items', + 'httpMethod' => Request::HTTP_METHOD_POST, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + ], + ]; + + $requestData = [ + 'cartItem' => [ + 'sku' => $productSku, + 'qty' => 1, + 'quote_id' => $cartId, + ], + ]; + $this->_webApiCall($serviceInfo, $requestData); + $this->assertTrue($quote->hasProductId('100000001')); + $this->assertFalse($quote->hasProductId('100000002')); + } +} diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_simple_out_of_stock.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_simple_out_of_stock.php new file mode 100644 index 0000000000000..6ef9b5cd5b0a8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_simple_out_of_stock.php @@ -0,0 +1,75 @@ +get(ProductRepositoryInterface::class); + +$productLinkFactory = Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\Data\ProductLinkInterfaceFactory::class); +$productConfigs = [ + [ + 'id' => '100000001', + 'stock_config' => ['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1] + ], + [ + 'id' => '100000002', + 'stock_config' => ['use_config_manage_stock' => 1, 'qty' => 0, 'is_qty_decimal' => 0, 'is_in_stock' => 0] + ] +]; + +foreach ($productConfigs as $productConfig) { + /** @var $product Product */ + $product = Bootstrap::getObjectManager()->create(Product::class); + $product->setTypeId(Type::TYPE_SIMPLE) + ->setId($productConfig['id']) + ->setWebsiteIds([1]) + ->setAttributeSetId(4) + ->setName('Simple ' . $productConfig['id']) + ->setSku('simple_' . $productConfig['id']) + ->setPrice(100) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData($productConfig['stock_config']); + + $linkedProducts[] = $productRepository->save($product); +} + +/** @var $product Product */ +$product = Bootstrap::getObjectManager()->create(Product::class); + +$product->setTypeId(Grouped::TYPE_CODE) + ->setId('100000003') + ->setWebsiteIds([1]) + ->setAttributeSetId(4) + ->setName('Grouped Product') + ->setSku('grouped') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]); + +foreach ($linkedProducts as $linkedProduct) { + /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */ + $productLink = $productLinkFactory->create(); + $productLink->setSku($product->getSku()) + ->setLinkType('associated') + ->setLinkedProductSku($linkedProduct->getSku()) + ->setLinkedProductType($linkedProduct->getTypeId()) + ->getExtensionAttributes() + ->setQty(1); + $newLinks[] = $productLink; +} + +$product->setProductLinks($newLinks); + +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_simple_out_of_stock_rollback.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_simple_out_of_stock_rollback.php new file mode 100644 index 0000000000000..b81c008cf1ab6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/_files/product_grouped_with_simple_out_of_stock_rollback.php @@ -0,0 +1,34 @@ +get(\Magento\Framework\Registry::class); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$skuList = ['simple_100000001', 'simple_100000002', 'grouped']; +foreach ($skuList as $sku) { + try { + $product = $productRepository->get($sku, false, null, true); + + $stockStatus = $objectManager->create(\Magento\CatalogInventory\Model\Stock\Status::class); + $stockStatus->load($product->getData('entity_id'), 'product_id'); + $stockStatus->delete(); + + $productRepository->delete($product); + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + //Product already removed + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From 96818778dbe268a023c99e9fdd9e3cd9a51dd51b Mon Sep 17 00:00:00 2001 From: Oleksii Korshenko Date: Wed, 31 Oct 2018 12:08:43 -0400 Subject: [PATCH 085/704] magento/magento2#18840: Invalid Unit Test Annotations - Updated composer.lock --- composer.lock | 130 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 113 insertions(+), 17 deletions(-) diff --git a/composer.lock b/composer.lock index c6d14eb38bc64..667050ffd6384 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "49fe82043cd4ae944939b6cceade1d1b", + "content-hash": "ca98d8e543436233c41b62a52bca9efa", "packages": [ { "name": "braintree/braintree_php", @@ -2064,7 +2064,7 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.9.0", + "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -2122,16 +2122,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.9.0", + "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8" + "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8", - "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", + "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", "shasum": "" }, "require": { @@ -2177,7 +2177,7 @@ "portable", "shim" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2018-09-21T13:07:52+00:00" }, { "name": "symfony/process", @@ -4616,6 +4616,56 @@ ], "time": "2016-12-07T12:15:46+00:00" }, + { + "name": "allure-framework/allure-phpunit", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/allure-framework/allure-phpunit.git", + "reference": "45504aeba41304cf155a898fa9ac1aae79f4a089" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/allure-framework/allure-phpunit/zipball/45504aeba41304cf155a898fa9ac1aae79f4a089", + "reference": "45504aeba41304cf155a898fa9ac1aae79f4a089", + "shasum": "" + }, + "require": { + "allure-framework/allure-php-api": "~1.1.0", + "mikey179/vfsstream": "1.*", + "php": ">=7.0.0", + "phpunit/phpunit": ">=6.0.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Yandex": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Ivan Krutov", + "email": "vania-pooh@yandex-team.ru", + "role": "Developer" + } + ], + "description": "A PHPUnit adapter for Allure report.", + "homepage": "http://allure.qatools.ru/", + "keywords": [ + "allure", + "attachments", + "cases", + "phpunit", + "report", + "steps", + "testing" + ], + "time": "2017-11-03T13:08:21+00:00" + }, { "name": "behat/gherkin", "version": "v4.4.5", @@ -6420,6 +6470,52 @@ ], "time": "2018-10-28T11:19:53+00:00" }, + { + "name": "mikey179/vfsStream", + "version": "v1.6.5", + "source": { + "type": "git", + "url": "https://github.com/mikey179/vfsStream.git", + "reference": "d5fec95f541d4d71c4823bb5e30cf9b9e5b96145" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mikey179/vfsStream/zipball/d5fec95f541d4d71c4823bb5e30cf9b9e5b96145", + "reference": "d5fec95f541d4d71c4823bb5e30cf9b9e5b96145", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "org\\bovigo\\vfs\\": "src/main/php" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Frank Kleine", + "homepage": "http://frankkleine.de/", + "role": "Developer" + } + ], + "description": "Virtual file system to mock the real file system in unit tests.", + "homepage": "http://vfs.bovigo.org/", + "time": "2017-08-01T08:02:14+00:00" + }, { "name": "moontoast/math", "version": "1.1.2", @@ -8637,16 +8733,16 @@ }, { "name": "symfony/polyfill-php70", - "version": "v1.9.0", + "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934" + "reference": "6b88000cdd431cd2e940caa2cb569201f3f84224" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/1e24b0c4a56d55aaf368763a06c6d1c7d3194934", - "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/6b88000cdd431cd2e940caa2cb569201f3f84224", + "reference": "6b88000cdd431cd2e940caa2cb569201f3f84224", "shasum": "" }, "require": { @@ -8692,20 +8788,20 @@ "portable", "shim" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2018-09-21T06:26:08+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.9.0", + "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "95c50420b0baed23852452a7f0c7b527303ed5ae" + "reference": "9050816e2ca34a8e916c3a0ae8b9c2fccf68b631" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/95c50420b0baed23852452a7f0c7b527303ed5ae", - "reference": "95c50420b0baed23852452a7f0c7b527303ed5ae", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9050816e2ca34a8e916c3a0ae8b9c2fccf68b631", + "reference": "9050816e2ca34a8e916c3a0ae8b9c2fccf68b631", "shasum": "" }, "require": { @@ -8747,7 +8843,7 @@ "portable", "shim" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2018-09-21T13:07:52+00:00" }, { "name": "symfony/stopwatch", From 5646ab9d0decf6012ba38dc6d8e974d4d0c51330 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Thu, 1 Nov 2018 10:01:32 +0200 Subject: [PATCH 086/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Fix fail of code compiler; --- app/code/Magento/Customer/Model/Customer/DataProvider.php | 7 +++---- app/code/Magento/Customer/Model/Metadata/Form/File.php | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Customer/Model/Customer/DataProvider.php b/app/code/Magento/Customer/Model/Customer/DataProvider.php index 7b770d20263dc..18e78c6bd7baf 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProvider.php +++ b/app/code/Magento/Customer/Model/Customer/DataProvider.php @@ -150,10 +150,9 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider private $allowToShowHiddenAttributes; /** - * DataProvider constructor. - * @param $name - * @param $primaryFieldName - * @param $requestFieldName + * @param string $name + * @param string $primaryFieldName + * @param string $requestFieldName * @param EavValidationRules $eavValidationRules * @param CustomerCollectionFactory $customerCollectionFactory * @param Config $eavConfig diff --git a/app/code/Magento/Customer/Model/Metadata/Form/File.php b/app/code/Magento/Customer/Model/Metadata/Form/File.php index b9bec01f9ba7c..3959d404d9f55 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/File.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/File.php @@ -68,7 +68,7 @@ class File extends AbstractData * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute * @param \Magento\Framework\Locale\ResolverInterface $localeResolver - * @param array|string $value + * @param null $value * @param string $entityTypeCode * @param bool $isAjax * @param \Magento\Framework\Url\EncoderInterface $urlEncoder From 24438ed8416bdc370096bae8b8a50425d4f9acd6 Mon Sep 17 00:00:00 2001 From: David Grigoryan Date: Thu, 1 Nov 2018 12:33:40 +0400 Subject: [PATCH 087/704] MAGETWO-95830: Cannot create credit memo if the used in the order cart rule is deleted - Add automated test --- ...reateCreditMemoWhenCartRuleDeletedTest.xml | 109 ------------------ 1 file changed, 109 deletions(-) delete mode 100644 app/code/Magento/Sales/Test/Mftf/Test/CreateCreditMemoWhenCartRuleDeletedTest.xml diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateCreditMemoWhenCartRuleDeletedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateCreditMemoWhenCartRuleDeletedTest.xml deleted file mode 100644 index 6ca7c838b7fe7..0000000000000 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateCreditMemoWhenCartRuleDeletedTest.xml +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - - - - - <description value="Verify Credit Memo created if the used in the order cart rule is deleted"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-95894"/> - <group value="sales"/> - </annotations> - <before> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="product"> - <requiredEntity createDataKey="createCategory"/> - </createData> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - - <!-- Create Cart Price Rule with a specific coupon --> - <actionGroup ref="AdminCreateCartPriceRuleWithCouponCode" stepKey="createCartPriceRule"> - <argument name="ruleName" value="TestSalesRule"/> - <argument name="couponCode" value="_defaultCoupon.code"/> - </actionGroup> - - <!--Go to Storefront. Add product to cart--> - <amOnPage url="/$$product.name$$.html" stepKey="GoToProduct"/> - <actionGroup ref="StorefrontAddToCartCustomOptionsProductPageActionGroup" stepKey="AddProductToCard"> - <argument name="productName" value="$$product.name$$"/> - </actionGroup> - <!--Proceed to checkout--> - <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> - <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - - <click selector="{{DiscountSection.DiscountTab}}" stepKey="clickToAddDiscount"/> - <fillField selector="{{DiscountSection.DiscountInput}}" userInput="{{_defaultCoupon.code}}" stepKey="TypeDiscountCode"/> - <click selector="{{DiscountSection.ApplyCodeBtn}}" stepKey="clickToApplyDiscount"/> - <waitForPageLoad stepKey="WaitForDiscountToBeAdded"/> - <see userInput="Your coupon was successfully applied." stepKey="verifyText"/> - - <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> - <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> - - <!--Proceed to Admin panel > SALES > Orders. Created order should be in Processing status--> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrdersIndexPage"/> - <waitForPageLoad stepKey="waitForOrderIndexPage"/> - - <!-- Open Order --> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> - <argument name="orderId" value="$grabOrderNumber"/> - </actionGroup> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForCreatedOrderPageOpened"/> - - <!--Click *Invoice* button--> - <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceButton"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seeNewInvoiceInPageTitle" after="clickInvoiceButton"/> - <waitForPageLoad stepKey="waitForInvoicePageOpened"/> - <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - <waitForPageLoad stepKey="waitForInvoiceSaved"/> - <see userInput="The invoice has been created." stepKey="seeCorrectMessage"/> - - <!-- Delete the cart price rule --> - <actionGroup ref="DeleteCartPriceRuleByName" stepKey="deleteCartPriceRule"> - <argument name="ruleName" value="{{TestSalesRule.name}}"/> - </actionGroup> - - <!--Proceed to Admin panel > SALES > Orders. Created order should be in Processing status--> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrdersIndexPage2"/> - <waitForPageLoad stepKey="waitForOrderIndexPage2"/> - - <!-- Open Order --> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById2"> - <argument name="orderId" value="$grabOrderNumber"/> - </actionGroup> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow2"/> - <waitForPageLoad stepKey="waitForCreatedOrderPageOpened2"/> - - <!--Admin create credit memo for order--> - <comment userInput="Admin creates credit memo" stepKey="createCreditMemoComment"/> - <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreditMemoAction"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Memo" stepKey="seeNewMemoInPageTitle"/> - <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickRefundOffline"/> - - <!--Make sure that Credit memo was created successfully--> - <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the credit memo." stepKey="seeCreditMemoSuccess"/> - - <after> - <deleteData createDataKey="product" stepKey="deleteProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logOut"/> - </after> - </test> -</tests> From 530d3f5356b09a2c8d0184a2f2da5d29d3224074 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Thu, 1 Nov 2018 11:48:04 +0200 Subject: [PATCH 088/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Static tests fix; --- .../Model/AttributeMetadataResolver.php | 14 ++--- .../Customer/Model/Metadata/Form/File.php | 2 +- .../Model/ResourceModel/Address/Relation.php | 56 +++++++++++++------ .../Unit/Model/Address/DataProviderTest.php | 3 + 4 files changed, 48 insertions(+), 27 deletions(-) diff --git a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php index 6b006d7f44e7e..c22cc9a4f23f4 100644 --- a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php +++ b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php @@ -189,16 +189,10 @@ private function canShowAttributeInForm(AbstractAttribute $customerAttribute, st if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { return \is_array($customerAttribute->getUsedInForms()) && ( - (\in_array( - 'customer_account_create', - $customerAttribute->getUsedInForms(), - true - ) && $isRegistration) || - (\in_array( - 'customer_account_edit', - $customerAttribute->getUsedInForms(), - true - ) && !$isRegistration) + (\in_array('customer_account_create', $customerAttribute->getUsedInForms(), true) + && $isRegistration) || + (\in_array('customer_account_edit', $customerAttribute->getUsedInForms(), true) + && !$isRegistration) ); } return \is_array($customerAttribute->getUsedInForms()) && diff --git a/app/code/Magento/Customer/Model/Metadata/Form/File.php b/app/code/Magento/Customer/Model/Metadata/Form/File.php index 3959d404d9f55..227e85ed98f91 100644 --- a/app/code/Magento/Customer/Model/Metadata/Form/File.php +++ b/app/code/Magento/Customer/Model/Metadata/Form/File.php @@ -68,7 +68,7 @@ class File extends AbstractData * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Customer\Api\Data\AttributeMetadataInterface $attribute * @param \Magento\Framework\Locale\ResolverInterface $localeResolver - * @param null $value + * @param string|array $value * @param string $entityTypeCode * @param bool $isAjax * @param \Magento\Framework\Url\EncoderInterface $urlEncoder diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php index 37a633d47f512..a2ba37e7ba1dd 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php @@ -7,6 +7,8 @@ */ namespace Magento\Customer\Model\ResourceModel\Address; +use Magento\Customer\Model\Address\AddressModelInterface; +use Magento\Customer\Model\Customer; use Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationInterface; /** @@ -40,23 +42,9 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) */ if (!$object->getIsCustomerSaveTransaction() && $object->getId()) { $customer = $this->customerFactory->create()->load($object->getCustomerId()); - $changedAddresses = []; - if ($object->getIsDefaultBilling()) { - $changedAddresses['default_billing'] = $object->getId(); - } elseif ($customer->getDefaultBillingAddress() - && (int)$customer->getDefaultBillingAddress()->getId() === (int)$object->getId() - ) { - $changedAddresses['default_billing'] = null; - } - - if ($object->getIsDefaultShipping()) { - $changedAddresses['default_shipping'] = $object->getId(); - } elseif ($customer->getDefaultShippingAddress() - && (int)$customer->getDefaultShippingAddress()->getId() === (int)$object->getId() - ) { - $changedAddresses['default_shipping'] = null; - } + $changedAddresses['default_billing'] = $this->getDefaultBillingChangedAddress($object, $customer); + $changedAddresses['default_shipping'] = $this->getDefaultShippingChangedAddress($object, $customer); if ($changedAddresses) { $customer->getResource()->getConnection()->update( @@ -68,6 +56,42 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) } } + /** + * Get default billing changed address + * + * @param AddressModelInterface $object + * @param Customer $customer + * @return int|null + */ + private function getDefaultBillingChangedAddress(AddressModelInterface $object, Customer $customer): ?int + { + if ($object->getIsDefaultBilling()) { + return $object->getId(); + } elseif ($customer->getDefaultBillingAddress() + && (int)$customer->getDefaultBillingAddress()->getId() === (int)$object->getId() + ) { + return null; + } + } + + /** + * Get default shipping changed address + * + * @param AddressModelInterface $object + * @param Customer $customer + * @return int|null + */ + private function getDefaultShippingChangedAddress(AddressModelInterface $object, Customer $customer): ?int + { + if ($object->getIsDefaultShipping()) { + return $object->getId(); + } elseif ($customer->getDefaultShippingAddress() + && (int)$customer->getDefaultShippingAddress()->getId() === (int)$object->getId() + ) { + return null; + } + } + /** * Checks if address has chosen as default and has had an id * diff --git a/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php index 81266a8934c87..92a15ac01fafa 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php @@ -17,6 +17,9 @@ use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Customer\Model\Address as AddressModel; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class DataProviderTest extends \PHPUnit\Framework\TestCase { /** From 89e371f3638d26d22df0b44af4a16a8adb0abdef Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Thu, 1 Nov 2018 12:12:18 +0200 Subject: [PATCH 089/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Static tests fix; --- .../Customer/Model/ResourceModel/Address/Grid/Collection.php | 1 + .../Customer/Test/Unit/Model/Address/DataProviderTest.php | 1 + .../Model/Customer/DataProviderWithDefaultAddressesTest.php | 1 + .../Test/Unit/Ui/Component/Form/AddressFieldsetTest.php | 2 +- app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php | 2 +- .../Magento/Customer/Controller/Adminhtml/Address/SaveTest.php | 1 + 6 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php index 8026349563867..09aef78b6cebc 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php @@ -1,4 +1,5 @@ <?php +declare(strict_types=1); /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. diff --git a/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php index 92a15ac01fafa..b1ac27802ff6d 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Address/DataProviderTest.php @@ -1,4 +1,5 @@ <?php +declare(strict_types=1); /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. diff --git a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php index eb1241de3e32c..add6f3525b95e 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderWithDefaultAddressesTest.php @@ -1,4 +1,5 @@ <?php +declare(strict_types=1); /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. diff --git a/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php b/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php index 31a8a1bfacc64..ba99203c5c6a4 100644 --- a/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php +++ b/app/code/Magento/Customer/Test/Unit/Ui/Component/Form/AddressFieldsetTest.php @@ -1,9 +1,9 @@ <?php +declare(strict_types=1); /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Customer\Test\Unit\Ui\Component\Form; use Magento\Customer\Ui\Component\Form\AddressFieldset; diff --git a/app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php b/app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php index 9a8cf28ae0719..4ad0b900762db 100644 --- a/app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php +++ b/app/code/Magento/Ui/Test/Unit/Component/Form/FieldsetTest.php @@ -1,9 +1,9 @@ <?php +declare(strict_types=1); /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Ui\Test\Unit\Component\Form; use Magento\Ui\Component\Form\Fieldset; diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php index 6a41dd70e89e2..c6741f76e0538 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php @@ -1,4 +1,5 @@ <?php +declare(strict_types=1); /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. From f3920b4c7dd4d50782f6f39415dc33ad4ea2c6aa Mon Sep 17 00:00:00 2001 From: David Grigoryan <david_grigoryan@epam.com> Date: Thu, 1 Nov 2018 16:28:05 +0400 Subject: [PATCH 090/704] MAGETWO-95812: Required RMA Attributes are not validated while Customer submits a return - Updated automated test --- ...lidationWhileCustomerSubmitsReturnTest.xml | 99 ------------------- 1 file changed, 99 deletions(-) delete mode 100644 app/code/Magento/Sales/Test/Mftf/Test/RequiredRMAAttributesValidationWhileCustomerSubmitsReturnTest.xml diff --git a/app/code/Magento/Sales/Test/Mftf/Test/RequiredRMAAttributesValidationWhileCustomerSubmitsReturnTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/RequiredRMAAttributesValidationWhileCustomerSubmitsReturnTest.xml deleted file mode 100644 index e3b2352c4c810..0000000000000 --- a/app/code/Magento/Sales/Test/Mftf/Test/RequiredRMAAttributesValidationWhileCustomerSubmitsReturnTest.xml +++ /dev/null @@ -1,99 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="RequiredRMAAttributesValidationWhileCustomerSubmitsReturnTest"> - <annotations> - <features value="Sales"/> - <stories value="MAGETWO-95812: Required RMA Attributes are not validated while Customer submits a return"/> - <title value="Required RMA Attributes Validation While Customer Submits A Return"/> - <description value="Required RMA Attributes Validation While Customer Submits A Return"/> - <severity value="MAJOR"/> - <testCaseId value="MAGETWO-95864"/> - <group value="sales"/> - </annotations> - <before> - <!-- log in as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!--Enabled RMA On Storefront--> - <createData entity="EnableRMA" stepKey="enabledRMAOnStorefront"/> - <!-- create new product --> - <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="SimpleProduct" stepKey="createSimpleProduct"> - <requiredEntity createDataKey="createCategory"/> - </createData> - </before> - - <!-- Place an order from frontend --> - - <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> - <argument name="product" value="$$createSimpleProduct$$"/> - </actionGroup> - - <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="checkoutProductFromCart"/> - - <actionGroup ref="GuestCheckoutFillingShippingSectionActionGroup" stepKey="guestCheckoutFillingShippingSection"> - <argument name="customerVar" value="CustomerEntityOne" /> - <argument name="customerAddressVar" value="CustomerAddressSimple" /> - </actionGroup> - - <!-- place for order --> - <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> - <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> - <waitForPageLoad stepKey="waitForPlaceOrder"/> - <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> - - <!--Complete the order from admin bay creating Invoice and then Shipment--> - <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrdersIndexPage"/> - <waitForPageLoad stepKey="waitForOrderIndexPage"/> - - <!-- Open Order --> - <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> - <argument name="orderId" value="$grabOrderNumber"/> - </actionGroup> - <click selector="{{AdminOrdersGridSection.firstRow}}" stepKey="clickOrderRow"/> - <waitForPageLoad stepKey="waitForCreatedOrderPageOpened"/> - - <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoiceAction"/> - <see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Invoice" stepKey="seePageNameNewInvoicePage"/> - <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="clickSubmitInvoice"/> - - <click selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="clickShipAction"/> - <seeInCurrentUrl url="{{AdminShipmentNewPage.url}}" stepKey="seeOrderShipmentUrl"/> - <click selector="{{AdminShipmentMainActionsSection.submitShipment}}" stepKey="clickSubmitShipment"/> - - <!--Goes to Orders and Returns --> - <amOnPage url="{{StorefrontOrdersAndReturnsPage.url}}" stepKey="goToOrderAndReturnPage"/> - <waitForPageLoad stepKey="waitForOrdersAndReturnsPageLoad"/> - <seeInCurrentUrl url="{{StorefrontOrdersAndReturnsPage.url}}" stepKey="seeOrdersAndReturnsUrl"/> - - <actionGroup ref="fillOrderInformationActionGroup" stepKey="fillOrderInformationAndContinue"> - <argument name="orderId" value="$grabOrderNumber"/> - <argument name="orderLastName" value="CustomerEntityOne.lastname"/> - <argument name="orderEmail" value="CustomerEntityOne.email"/> - </actionGroup> - - <click selector="{{OrderInformationMainSection.return}}" stepKey="clickOnReturn"/> - <actionGroup ref="fillQuantityToReturnActionGroup" stepKey="fillQuantityToReturn"/> - - <seeElement selector="{{CreateNewReturnMainSection.resolutionError}}" stepKey="AssertResolutionError" /> - <seeElement selector="{{CreateNewReturnMainSection.conditionError}}" stepKey="AssertConditionError" /> - <seeElement selector="{{CreateNewReturnMainSection.reasonError}}" stepKey="AssertReasonError" /> - - <after> - <!--Disable RMA On Storefront--> - <createData entity="DisableRMA" stepKey="disableRMAOnStorefront"/> - - <!--Delete the created product--> - <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> - <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - </test> -</tests> From 58514a0f25c7596d98b8b135eefa1564f9ed7935 Mon Sep 17 00:00:00 2001 From: shikhamis11 <shikhamishra@cedcoss.com> Date: Thu, 1 Nov 2018 17:59:28 +0530 Subject: [PATCH 091/704] fixed store wise product filter issue Fixed issue - #18374 Unable to get product attribute value for store-view scope type in product collection loaded for a specific store. --- .../Magento/Eav/Model/Entity/Collection/AbstractCollection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php index 0eb87374f3ba3..eba9976260bed 100644 --- a/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php +++ b/app/code/Magento/Eav/Model/Entity/Collection/AbstractCollection.php @@ -399,7 +399,7 @@ public function addAttributeToFilter($attribute, $condition = null, $joinType = */ public function addFieldToFilter($attribute, $condition = null) { - return $this->addAttributeToFilter($attribute, $condition); + return $this->addAttributeToFilter($attribute, $condition,'left'); } /** From 83c4dadf8fc825fbddad9e1361930f38fb37d453 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Thu, 1 Nov 2018 19:05:12 +0200 Subject: [PATCH 092/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Fix address relation model; - Unit tests updated; --- .../Adminhtml/File/Address/Upload.php | 3 +- .../Model/ResourceModel/Address/Relation.php | 45 ++++++++++++------- .../Adminhtml/File/Address/UploadTest.php | 40 +++++++++-------- .../ResourceModel/Address/RelationTest.php | 16 ++++++- 4 files changed, 66 insertions(+), 38 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php b/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php index afa62d2148eb4..e9034c8050383 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/File/Address/Upload.php @@ -11,6 +11,7 @@ use Magento\Customer\Model\FileUploader; use Magento\Customer\Model\FileUploaderFactory; use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Exception\LocalizedException; use Psr\Log\LoggerInterface; @@ -18,7 +19,7 @@ /** * Uploads files for customer address */ -class Upload extends Action implements HttpGetActionInterface +class Upload extends Action implements HttpGetActionInterface, HttpPostActionInterface { /** * Authorization level of a basic admin session diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php index a2ba37e7ba1dd..a82a814d40772 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php @@ -7,7 +7,7 @@ */ namespace Magento\Customer\Model\ResourceModel\Address; -use Magento\Customer\Model\Address\AddressModelInterface; +use Magento\Customer\Model\Address; use Magento\Customer\Model\Customer; use Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationInterface; @@ -38,13 +38,14 @@ public function __construct(\Magento\Customer\Model\CustomerFactory $customerFac public function processRelation(\Magento\Framework\Model\AbstractModel $object) { /** - * @var $object \Magento\Customer\Model\Address + * @var $object Address */ if (!$object->getIsCustomerSaveTransaction() && $object->getId()) { $customer = $this->customerFactory->create()->load($object->getCustomerId()); - $changedAddresses['default_billing'] = $this->getDefaultBillingChangedAddress($object, $customer); - $changedAddresses['default_shipping'] = $this->getDefaultShippingChangedAddress($object, $customer); + $changedAddresses = []; + $changedAddresses = $this->getDefaultBillingChangedAddress($object, $customer, $changedAddresses); + $changedAddresses = $this->getDefaultShippingChangedAddress($object, $customer, $changedAddresses); if ($changedAddresses) { $customer->getResource()->getConnection()->update( @@ -59,37 +60,49 @@ public function processRelation(\Magento\Framework\Model\AbstractModel $object) /** * Get default billing changed address * - * @param AddressModelInterface $object + * @param Address $object * @param Customer $customer - * @return int|null + * @param array $changedAddresses + * @return array */ - private function getDefaultBillingChangedAddress(AddressModelInterface $object, Customer $customer): ?int - { + private function getDefaultBillingChangedAddress( + Address $object, + Customer $customer, + array $changedAddresses + ): array { if ($object->getIsDefaultBilling()) { - return $object->getId(); + $changedAddresses['default_billing'] = $object->getId(); } elseif ($customer->getDefaultBillingAddress() && (int)$customer->getDefaultBillingAddress()->getId() === (int)$object->getId() ) { - return null; + $changedAddresses['default_billing'] = null; } + + return $changedAddresses; } /** * Get default shipping changed address * - * @param AddressModelInterface $object + * @param Address $object * @param Customer $customer - * @return int|null + * @param array $changedAddresses + * @return array */ - private function getDefaultShippingChangedAddress(AddressModelInterface $object, Customer $customer): ?int - { + private function getDefaultShippingChangedAddress( + Address $object, + Customer $customer, + array $changedAddresses + ): array { if ($object->getIsDefaultShipping()) { - return $object->getId(); + $changedAddresses['default_shipping'] = $object->getId(); } elseif ($customer->getDefaultShippingAddress() && (int)$customer->getDefaultShippingAddress()->getId() === (int)$object->getId() ) { - return null; + $changedAddresses['default_shipping'] = null; } + + return $changedAddresses; } /** diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/File/Address/UploadTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/File/Address/UploadTest.php index 20177ab0b0db6..8f8ed0e37a46b 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/File/Address/UploadTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/File/Address/UploadTest.php @@ -70,7 +70,8 @@ protected function setUp() $this->context, $this->fileUploaderFactory, $this->addressMetadataService, - $this->logger + $this->logger, + 'address' ); } @@ -104,27 +105,27 @@ public function testExecuteEmptyFiles() public function testExecute() { - $attributeCode = 'attribute_code'; + $attributeCode = 'file_address_attribute'; + $resultFileSize = 20000; + $resultFileName = 'text.txt'; + $resultType = 'text/plain'; $_FILES = [ - 'address' => [ - 'name' => [ - 'new_0' => [ - $attributeCode => 'filename', - ], - ], + $attributeCode => [ + 'name' => $resultFileName, + 'type' => $resultType, + 'size' => $resultFileSize ], ]; - $resultFileName = '/filename.ext1'; $resultFilePath = 'filepath'; $resultFileUrl = 'viewFileUrl'; $result = [ 'name' => $resultFileName, - 'file' => $resultFileName, - 'path' => $resultFilePath, - 'tmp_name' => $resultFilePath . $resultFileName, + 'type' => $resultType, + 'size' => $resultFileSize, + 'tmp_name' => $resultFilePath . '/' . $resultFileName, 'url' => $resultFileUrl, ]; @@ -173,15 +174,16 @@ public function testExecute() public function testExecuteWithErrors() { - $attributeCode = 'attribute_code'; + $attributeCode = 'file_address_attribute'; + $resultFileSize = 20000; + $resultFileName = 'text.txt'; + $resultType = 'text/plain'; $_FILES = [ - 'address' => [ - 'name' => [ - 'new_0' => [ - $attributeCode => 'filename', - ], - ], + $attributeCode => [ + 'name' => $resultFileName, + 'type' => $resultType, + 'size' => $resultFileSize ], ]; diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/Address/RelationTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/Address/RelationTest.php index e81637cfb23b2..319179c5e279a 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/Address/RelationTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/Address/RelationTest.php @@ -6,6 +6,7 @@ namespace Magento\Customer\Test\Unit\Model\ResourceModel\Address; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Customer\Model\Address; /** * Class AddressTest @@ -40,7 +41,7 @@ protected function setUp() */ public function testProcessRelation($addressId, $isDefaultBilling, $isDefaultShipping) { - $addressModel = $this->createPartialMock(\Magento\Framework\Model\AbstractModel::class, [ + $addressModel = $this->createPartialMock(Address::class, [ '__wakeup', 'getId', 'getEntityTypeId', @@ -55,7 +56,17 @@ public function testProcessRelation($addressId, $isDefaultBilling, $isDefaultShi ]); $customerModel = $this->createPartialMock( \Magento\Customer\Model\Customer::class, - ['__wakeup', 'setDefaultBilling', 'setDefaultShipping', 'save', 'load', 'getResource', 'getId'] + [ + '__wakeup', + 'setDefaultBilling', + 'setDefaultShipping', + 'save', + 'load', + 'getResource', + 'getId', + 'getDefaultShippingAddress', + 'getDefaultBillingAddress' + ] ); $customerResource = $this->getMockForAbstractClass( \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, @@ -88,6 +99,7 @@ public function testProcessRelation($addressId, $isDefaultBilling, $isDefaultShi $this->customerFactoryMock->expects($this->any()) ->method('create') ->willReturn($customerModel); + if ($addressId && ($isDefaultBilling || $isDefaultShipping)) { $customerId = 1; $customerResource->expects($this->exactly(2))->method('getConnection')->willReturn($connectionMock); From fc5eb486c48c3c6fd709d2a042b101e0bf32c976 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov <lpoluyanov@magento.com> Date: Fri, 2 Nov 2018 10:39:49 +0200 Subject: [PATCH 093/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Index in db_schema.xml fixed --- app/code/Magento/Customer/etc/db_schema.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/etc/db_schema.xml b/app/code/Magento/Customer/etc/db_schema.xml index 7e0b911e26184..7cf4c5be641e8 100644 --- a/app/code/Magento/Customer/etc/db_schema.xml +++ b/app/code/Magento/Customer/etc/db_schema.xml @@ -120,8 +120,8 @@ <index referenceId="CUSTOMER_ADDRESS_ENTITY_PARENT_ID" indexType="btree"> <column name="parent_id"/> </index> - <index name="CUSTOMER_ADDRESS_ENTITY_FULLTEXT_INDEX" indexType="fulltext"> - <column name="firstname"/> + <index referenceId="CUSTOMER_ADDRESS_ENTITY_FULLTEXT_INDEX" indexType="fulltext"> + <column name="firstname"/> <column name="lastname"/> <column name="street"/> <column name="city"/> From 57e59d8044cca1d85873ecde5d6a00660f4ffb31 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov <lpoluyanov@magento.com> Date: Fri, 2 Nov 2018 15:20:27 +0200 Subject: [PATCH 094/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Default addresses changes --- .../Model/Customer/DataProviderWithDefaultAddresses.php | 4 ++-- .../Magento/Customer/Model/ResourceModel/Address/Relation.php | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php index eafa5907efac9..30edafe3d504c 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php +++ b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php @@ -24,7 +24,7 @@ class DataProviderWithDefaultAddresses extends \Magento\Ui\DataProvider\Abstract /** * @var array */ - private $loadedData; + private $loadedData = []; /** * @var SessionManagerInterface @@ -114,7 +114,7 @@ public function __construct( */ public function getData(): array { - if (null !== $this->loadedData) { + if (!empty($this->loadedData)) { return $this->loadedData; } $items = $this->collection->getItems(); diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php index a82a814d40772..ae342a1b10dd8 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Relation.php @@ -73,6 +73,7 @@ private function getDefaultBillingChangedAddress( if ($object->getIsDefaultBilling()) { $changedAddresses['default_billing'] = $object->getId(); } elseif ($customer->getDefaultBillingAddress() + && $object->getIsDefaultBilling() === false && (int)$customer->getDefaultBillingAddress()->getId() === (int)$object->getId() ) { $changedAddresses['default_billing'] = null; @@ -97,6 +98,7 @@ private function getDefaultShippingChangedAddress( if ($object->getIsDefaultShipping()) { $changedAddresses['default_shipping'] = $object->getId(); } elseif ($customer->getDefaultShippingAddress() + && $object->getIsDefaultShipping() === false && (int)$customer->getDefaultShippingAddress()->getId() === (int)$object->getId() ) { $changedAddresses['default_shipping'] = null; From 43e2c4dae51a62e169eed7ae6761f565d6780692 Mon Sep 17 00:00:00 2001 From: StasKozar <staskozar.91@gmail.com> Date: Fri, 2 Nov 2018 17:31:24 +0200 Subject: [PATCH 095/704] magento/magento2#18990: Shipping address is not validated in checkout when proceeding step as logged in user with default shipping address --- .../Checkout/Test/Mftf/Data/CountryData.xml | 22 ++++++++ ...frontCustomerCheckoutWithoutRegionTest.xml | 55 +++++++++++++++++++ .../view/frontend/web/js/view/shipping.js | 15 ++++- .../GeneralConfigurationActionGroup.xml | 13 +++++ .../Test/Mftf/Section/GeneralSection.xml | 5 ++ .../Customer/Test/Mftf/Data/CustomerData.xml | 13 +++++ 6 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Data/CountryData.xml b/app/code/Magento/Checkout/Test/Mftf/Data/CountryData.xml index 26bc6ff641a9c..dc82932ec5ca7 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Data/CountryData.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Data/CountryData.xml @@ -13,4 +13,26 @@ <item>Bahamas</item> </array> </entity> + <entity name="DefaultCountriesWithRequiredRegions" type="countryArray"> + <array key="country"> + <item>Australia</item> + <item>Brazil</item> + <item>Canada</item> + <item>Croatia</item> + <item>Estonia</item> + <item>India</item> + <item>Latvia</item> + <item>Lithuania</item> + <item>Romania</item> + <item>Spain</item> + <item>Switzerland</item> + <item>United States</item> + <item>Australia</item> + </array> + </entity> + <entity name="CustomCountryWithRequiredRegion" type="countryArray"> + <array key="country"> + <item>United Kingdom</item> + </array> + </entity> </entities> \ No newline at end of file diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml new file mode 100644 index 0000000000000..0cc0dcf38e312 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutWithoutRegionTest.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCustomerCheckoutWithoutRegionTest"> + <annotations> + <features value="Checkout"/> + <stories value="Checkout via the Admin"/> + <title value="Shipping address is not validated in checkout when proceeding step as logged in user with default shipping address"/> + <description value="Shouldn't be able to place an order as a customer without state if it's required."/> + <severity value="CRITICAL"/> + <testCaseId value="#"/> + <group value="checkout"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="Simple_GB_Customer" stepKey="createCustomer"/> + <actionGroup stepKey="loginToAdminPanel" ref="LoginAsAdmin"/> + <actionGroup ref="SelectCountriesWithRequiredRegion" stepKey="setCustomCountryWithRequiredRegion"> + <argument name="countries" value="CustomCountryWithRequiredRegion"/> + </actionGroup> + </before> + <after> + <actionGroup ref="SelectCountriesWithRequiredRegion" stepKey="setDefaultCountriesWithRequiredRegion"> + <argument name="countries" value="DefaultCountriesWithRequiredRegions"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + </after> + + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> + <argument name="Customer" value="$$createCustomer$$" /> + </actionGroup> + + <actionGroup ref="AddSimpleProductToCart" stepKey="addProductToCart"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="navigateToCheckoutPage"/> + + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNextButton"/> + <see selector="{{StorefrontMessagesSection.error}}" userInput='Please specify a regionId in shipping address.' stepKey="seeErrorMessages"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js index 395d15bc02f36..6ac805f72f78d 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js @@ -265,7 +265,12 @@ define([ addressData, loginFormSelector = 'form[data-role=email-with-possible-login]', emailValidationResult = customer.isLoggedIn(), - field; + field, + countryIndexedOptions = registry.get( + this.parentName + '.shippingAddress.shipping-address-fieldset.country_id' + ).indexedOptions, + option = countryIndexedOptions[quote.shippingAddress().countryId], + messageContainer = registry.get('checkout.errors').messageContainer; if (!quote.shippingMethod()) { this.errorValidationMessage( @@ -318,6 +323,14 @@ define([ shippingAddress['save_in_address_book'] = 1; } selectShippingAddress(shippingAddress); + } else if (customer.isLoggedIn() + && option + && option['is_region_required'] + && !quote.shippingAddress().region + ) { + messageContainer.addErrorMessage({message: $t('Please specify a regionId in shipping address.')}); + + return false; } if (!emailValidationResult) { diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml index fca48dfb49cfb..f05cf5be3448e 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml @@ -37,4 +37,17 @@ <click selector="#save" stepKey="saveConfig"/> <waitForPageLoad stepKey="waitForSavingConfig"/> </actionGroup> + + <actionGroup name="SelectCountriesWithRequiredRegion"> + <arguments> + <argument name="countries" type="countryArray"/> + </arguments> + <amOnPage url="{{AdminConfigGeneralPage.url}}" stepKey="navigateToAdminConfigGeneralPage"/> + <conditionalClick selector="{{StateOptionsSection.stateOptions}}" dependentSelector="{{StateOptionsSection.countriesWithRequiredRegions}}" visible="false" stepKey="expandStateOptionsTab" /> + <waitForAjaxLoad stepKey="waitForAjax"/> + <scrollTo selector="{{StateOptionsSection.countriesWithRequiredRegions}}" stepKey="scrollToForm"/> + <selectOption selector="{{StateOptionsSection.countriesWithRequiredRegions}}" parameterArray="[{{countries.country}}]" stepKey="selectCountriesWithRequiredRegion"/> + <click selector="#save" stepKey="saveConfig"/> + <waitForPageLoad stepKey="waitForSavingConfig"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml index 88532dc091f45..66b40f74d8e98 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/GeneralSection.xml @@ -38,4 +38,9 @@ <element name="countryOptionsOpen" type="button" selector="#general_country-head.open"/> <element name="topDestinations" type="select" selector="#general_country_destinations"/> </section> + <section name="StateOptionsSection"> + <element name="stateOptions" type="button" selector="#general_region-head"/> + <element name="countriesWithRequiredRegions" type="select" selector="#general_region_state_required"/> + <element name="allowToChooseState" type="select" selector="general_region_display_all"/> + </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index 6f5ade53e6790..a61871105a5f3 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -102,4 +102,17 @@ <data key="website_id">0</data> <requiredEntity type="address">US_Address_CA</requiredEntity> </entity> + <entity name="Simple_GB_Customer" type="customer"> + <data key="group_id">0</data> + <data key="default_billing">true</data> + <data key="default_shipping">true</data> + <data key="email" unique="prefix">Jane.Doe@example.com</data> + <data key="firstname">Jane</data> + <data key="lastname">Doe</data> + <data key="fullname">Jane Doe</data> + <data key="password">pwdTest123!</data> + <data key="store_id">0</data> + <data key="website_id">0</data> + <requiredEntity type="address">UK_Not_Default_Address</requiredEntity> + </entity> </entities> From b9367a6bab6272cc4efada12370181aaf19fa9d7 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Mon, 5 Nov 2018 10:31:23 -0600 Subject: [PATCH 096/704] MAGETWO-93181: Grouped product doesn't take care about his Linked Products when SalableQuantity < ProductLink.ExtensionAttributes.Qty after Source Deduction - Fixed namespace --- .../Magento/GroupedProduct/Api/CartItemRepositoryTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/CartItemRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/CartItemRepositoryTest.php index 61f07ac3aa043..602493481449f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/CartItemRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/CartItemRepositoryTest.php @@ -4,7 +4,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\GroupedProduce\Api; +namespace Magento\GroupedProduct\Api; use Magento\Catalog\Model\CustomOptions\CustomOptionProcessor; use Magento\Framework\Webapi\Rest\Request; From 3785225428b54e81f0b6a09e0528af0978354044 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Mon, 5 Nov 2018 18:33:04 +0200 Subject: [PATCH 097/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Static fixes; --- .../adminhtml/web/js/address/default-address.js | 16 ++++++++-------- .../Magento_Customer/web/css/source/_module.less | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js index b4ec734cb4033..530df8544c841 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js @@ -10,8 +10,8 @@ define([ return Button.extend({ defaults: { - entity_id: null, - parent_id: null + entityId: null, + parentId: null }, /** @@ -21,8 +21,8 @@ define([ */ initialize: function () { this._super(); - if (!this.parent_id) { - this.visible(this.entity_id); + if (!this.parentId) { + this.visible(this.entityId); } return this; @@ -36,12 +36,12 @@ define([ */ applyAction: function (action) { if (action.params && action.params[0]) { - action.params[0].entity_id = this.entity_id; - action.params[0].parent_id = this.parent_id; + action.params[0].entityId = this.entityId; + action.params[0].parentId = this.parentId; } else { action.params = [{ - entity_id: this.entity_id, - parent_id: this.parent_id + entityId: this.entityId, + parentId: this.parentId }]; } diff --git a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less index 876859ff38d1a..7cb02b61fdbd8 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less +++ b/app/design/adminhtml/Magento/backend/Magento_Customer/web/css/source/_module.less @@ -16,8 +16,8 @@ .customer-address-form { *, - *:before, - *:after { + *:after, + *:before { box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; From 3aa0ace149dc76c0a4873fef1b507d13a1122690 Mon Sep 17 00:00:00 2001 From: Oleksii Korshenko <korshenk@adobe.com> Date: Mon, 5 Nov 2018 10:50:10 -0600 Subject: [PATCH 098/704] Fixed incorrect annotations in integration tests --- .../CatalogImportExport/Model/Export/ProductTest.php | 6 +++--- .../Magento/Setup/Declaration/WhitelistDeclarationTest.php | 2 +- .../testsuite/Magento/Test/Integrity/StaticFilesTest.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php index 70e0cf0e1e74e..c212d4c0d971a 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php @@ -70,7 +70,7 @@ protected function setUp() /** * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_data.php - * @magentoDbIsolationEnabled + * @magentoDbIsolation enabled */ public function testExport() { @@ -95,7 +95,7 @@ public function testExport() /** * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_data_special_chars.php - * @magentoDbIsolationEnabled + * @magentoDbIsolation enabled */ public function testExportSpecialChars() { @@ -111,7 +111,7 @@ public function testExportSpecialChars() /** * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_with_product_links_data.php - * @magentoDbIsolationEnabled + * @magentoDbIsolation enabled */ public function testExportWithProductLinks() { diff --git a/dev/tests/integration/testsuite/Magento/Setup/Declaration/WhitelistDeclarationTest.php b/dev/tests/integration/testsuite/Magento/Setup/Declaration/WhitelistDeclarationTest.php index 6b92e687284db..bebfb11c42a32 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Declaration/WhitelistDeclarationTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Declaration/WhitelistDeclarationTest.php @@ -47,7 +47,7 @@ public function setUp() /** * Checks that all declared table elements also declared into whitelist declaration. * - * @appIsolation + * @magentoAppIsolation * @throws \Exception */ public function testConstraintsAndIndexesAreWhitelisted() diff --git a/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php b/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php index 2afb6e90667dc..eee166ab341a9 100644 --- a/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php +++ b/dev/tests/integration/testsuite/Magento/Test/Integrity/StaticFilesTest.php @@ -76,7 +76,7 @@ protected function setUp() /** * Scan references to files from other static files and assert they are correct * - * The CSS or LESS files may refer to other resources using @import or url() notation + * The CSS or LESS files may refer to other resources using `import` or url() notation * We want to check integrity of all these references * Note that the references may have syntax specific to the Magento preprocessing subsystem * From ab4ad4d63a4724d03f52f6a42510c0dacbb83e4f Mon Sep 17 00:00:00 2001 From: Oleksii Korshenko <korshenk@adobe.com> Date: Mon, 5 Nov 2018 13:31:20 -0600 Subject: [PATCH 099/704] Fixed incorrect annotations in integration tests --- .../Magento/CatalogImportExport/Model/Import/ProductTest.php | 3 --- .../Magento/Setup/Declaration/WhitelistDeclarationTest.php | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index a48b5d43e501b..ef8733b26a7e9 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -762,7 +762,6 @@ protected function getOptionValues(\Magento\Catalog\Model\Product\Option $option /** * Test that product import with images works properly * - * @magentoDataIsolation enabled * @magentoDataFixture mediaImportImageFixture * @magentoAppIsolation enabled * @SuppressWarnings(PHPMD.ExcessiveMethodLength) @@ -813,7 +812,6 @@ public function testSaveMediaImage() /** * Test that errors occurred during importing images are logged. * - * @magentoDataIsolation enabled * @magentoAppIsolation enabled * @magentoDataFixture mediaImportImageFixture * @magentoDataFixture mediaImportImageFixtureError @@ -2211,7 +2209,6 @@ public function testImportWithDifferentSkuCase() /** * Test that product import with images for non-default store works properly. * - * @magentoDataIsolation enabled * @magentoDataFixture mediaImportImageFixture * @magentoAppIsolation enabled */ diff --git a/dev/tests/integration/testsuite/Magento/Setup/Declaration/WhitelistDeclarationTest.php b/dev/tests/integration/testsuite/Magento/Setup/Declaration/WhitelistDeclarationTest.php index bebfb11c42a32..04f25f231c933 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Declaration/WhitelistDeclarationTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Declaration/WhitelistDeclarationTest.php @@ -47,7 +47,7 @@ public function setUp() /** * Checks that all declared table elements also declared into whitelist declaration. * - * @magentoAppIsolation + * @magentoAppIsolation enabled * @throws \Exception */ public function testConstraintsAndIndexesAreWhitelisted() From 39055807e508b33d658f18e77caec732b9aaca2b Mon Sep 17 00:00:00 2001 From: Giel Berkers <giel.berkers@isaac.nl> Date: Tue, 6 Nov 2018 09:53:55 +0100 Subject: [PATCH 100/704] [BUGFIX] Forward-port of #14861 for Magento 2.3 --- .../ResourceModel/Product/CategoryLink.php | 12 ++- .../Product/CategoryLinkTest.php | 98 ++++++++++++++++++- 2 files changed, 101 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/CategoryLink.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/CategoryLink.php index b54c19a111508..d4c8ada22529f 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/CategoryLink.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/CategoryLink.php @@ -115,10 +115,14 @@ private function processCategoryLinks($newCategoryPositions, &$oldCategoryPositi { $result = ['changed' => [], 'updated' => []]; foreach ($newCategoryPositions as $newCategoryPosition) { - $key = array_search( - $newCategoryPosition['category_id'], - array_column($oldCategoryPositions, 'category_id') - ); + $key = false; + + foreach ($oldCategoryPositions as $oldKey => $oldCategoryPosition) { + if ((int)$oldCategoryPosition['category_id'] === (int)$newCategoryPosition['category_id']) { + $key = $oldKey; + break; + } + } if ($key === false) { $result['changed'][] = $newCategoryPosition; diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CategoryLinkTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CategoryLinkTest.php index 9e2b196602993..c0c061ab8a704 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CategoryLinkTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CategoryLinkTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Catalog\Test\Unit\Model\ResourceModel\Product; use Magento\Catalog\Model\ResourceModel\Product\CategoryLink; @@ -129,9 +130,65 @@ public function testSaveCategoryLinks($newCategoryLinks, $dbCategoryLinks, $affe ); } + $expectedResult = []; + + foreach ($affectedIds as $type => $ids) { + $expectedResult = array_merge($expectedResult, $ids); + + // Verify if the correct insert, update and/or delete actions are performed: + switch ($type) { + case 'insert': + $this->connectionMock + ->expects($this->exactly(empty($ids) ? 0 : 1)) + ->method('insertArray') + ->with( + $this->anything(), + $this->anything(), + $this->callback(function ($data) use ($ids) { + $foundIds = []; + foreach ($data as $row) { + $foundIds[] = $row['category_id']; + } + return $ids === $foundIds; + }) + ); + break; + case 'update': + $this->connectionMock + ->expects($this->exactly(empty($ids) ? 0 : 1)) + ->method('insertOnDuplicate') + ->with( + $this->anything(), + $this->callback(function ($data) use ($ids) { + $foundIds = []; + foreach ($data as $row) { + $foundIds[] = $row['category_id']; + } + return $ids === $foundIds; + }) + ); + break; + case 'delete': + $this->connectionMock + ->expects($this->exactly(empty($ids) ? 0 : 1)) + ->method('delete') + // Verify that the correct category ID's are touched: + ->with( + $this->anything(), + $this->callback(function ($data) use ($ids) { + return array_values($data)[1] === $ids; + }) + ); + break; + } + } + $actualResult = $this->model->saveCategoryLinks($product, $newCategoryLinks); + sort($actualResult); - $this->assertEquals($affectedIds, $actualResult); + sort($expectedResult); + + $this->assertEquals($expectedResult, $actualResult); } /** @@ -151,7 +208,11 @@ public function getCategoryLinksDataProvider() ['category_id' => 3, 'position' => 10], ['category_id' => 4, 'position' => 20], ], - [], // Nothing to update - data not changed + [ + 'update' => [], + 'insert' => [], + 'delete' => [], + ], ], [ [ @@ -162,7 +223,11 @@ public function getCategoryLinksDataProvider() ['category_id' => 3, 'position' => 10], ['category_id' => 4, 'position' => 20], ], - [3, 4, 5], // 4 - updated position, 5 - added, 3 - deleted + [ + 'update' => [4], + 'insert' => [5], + 'delete' => [3], + ], ], [ [ @@ -173,7 +238,11 @@ public function getCategoryLinksDataProvider() ['category_id' => 3, 'position' => 10], ['category_id' => 4, 'position' => 20], ], - [3, 4], // 3 - updated position, 4 - deleted + [ + 'update' => [3], + 'insert' => [], + 'delete' => [4], + ], ], [ [], @@ -181,8 +250,27 @@ public function getCategoryLinksDataProvider() ['category_id' => 3, 'position' => 10], ['category_id' => 4, 'position' => 20], ], - [3, 4], // 3, 4 - deleted + [ + 'update' => [], + 'insert' => [], + 'delete' => [3, 4], + ], ], + [ + [ + ['category_id' => 3, 'position' => 10], + ['category_id' => 4, 'position' => 20], + ], + [ + ['category_id' => 3, 'position' => 20], // swapped positions + ['category_id' => 4, 'position' => 10], // swapped positions + ], + [ + 'update' => [3, 4], + 'insert' => [], + 'delete' => [], + ], + ] ]; } } From 965f838c0e993c8a588f22cc5edc2db55f936a44 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov <lpoluyanov@magento.com> Date: Tue, 6 Nov 2018 11:45:36 +0200 Subject: [PATCH 101/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Ability to save addresses through CustomerRepository rolled back - Integration tests fixes --- .../ResourceModel/CustomerRepository.php | 57 +++- .../Customer/etc/db_schema_whitelist.json | 3 +- .../ui_component/customer_address_listing.xml | 29 +- .../view/base/ui_component/customer_form.xml | 9 +- .../Controller/Adminhtml/Address/SaveTest.php | 36 ++- .../Controller/Adminhtml/IndexTest.php | 275 +----------------- 6 files changed, 106 insertions(+), 303 deletions(-) diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php index 5d88cd92c1730..16c650be15b61 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php @@ -170,18 +170,22 @@ public function save(CustomerInterface $customer, $passwordHash = null) /** @var NewOperation|null $delegatedNewOperation */ $delegatedNewOperation = !$customer->getId() ? $this->delegatedStorage->consumeNewOperation() : null; $prevCustomerData = null; + $prevCustomerDataArr = null; if ($customer->getId()) { $prevCustomerData = $this->getById($customer->getId()); + $prevCustomerDataArr = $prevCustomerData->__toArray(); } - + /** @var $customer \Magento\Customer\Model\Data\Customer */ + $customerArr = $customer->__toArray(); $customer = $this->imageProcessor->save( $customer, CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, $prevCustomerData ); - - /** @var array $customerData */ + $origAddresses = $customer->getAddresses(); + $customer->setAddresses([]); $customerData = $this->extensibleDataObjectConverter->toNestedArray($customer, [], CustomerInterface::class); + $customer->setAddresses($origAddresses); /** @var Customer $customerModel */ $customerModel = $this->customerFactory->create(['data' => $customerData]); //Model's actual ID field maybe different than "id" so "id" field from $customerData may be ignored. @@ -190,16 +194,61 @@ public function save(CustomerInterface $customer, $passwordHash = null) if ($storeId === null) { $customerModel->setStoreId($this->storeManager->getStore()->getId()); } + // Need to use attribute set or future updates can cause data loss + if (!$customerModel->getAttributeSetId()) { + $customerModel->setAttributeSetId(CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER); + } $this->populateCustomerWithSecureData($customerModel, $passwordHash); // If customer email was changed, reset RpToken info if ($prevCustomerData && $prevCustomerData->getEmail() !== $customerModel->getEmail()) { $customerModel->setRpToken(null); $customerModel->setRpTokenCreatedAt(null); } + if (!array_key_exists('default_billing', $customerArr) + && null !== $prevCustomerDataArr + && array_key_exists('default_billing', $prevCustomerDataArr) + ) { + $customerModel->setDefaultBilling($prevCustomerDataArr['default_billing']); + } + if (!array_key_exists('default_shipping', $customerArr) + && null !== $prevCustomerDataArr + && array_key_exists('default_shipping', $prevCustomerDataArr) + ) { + $customerModel->setDefaultShipping($prevCustomerDataArr['default_shipping']); + } $customerModel->save(); $this->customerRegistry->push($customerModel); $customerId = $customerModel->getId(); - + if (!$customer->getAddresses() + && $delegatedNewOperation + && $delegatedNewOperation->getCustomer()->getAddresses() + ) { + $customer->setAddresses($delegatedNewOperation->getCustomer()->getAddresses()); + } + if ($customer->getAddresses() !== null) { + if ($customer->getId()) { + $existingAddresses = $this->getById($customer->getId())->getAddresses(); + $getIdFunc = function ($address) { + return $address->getId(); + }; + $existingAddressIds = array_map($getIdFunc, $existingAddresses); + } else { + $existingAddressIds = []; + } + $savedAddressIds = []; + foreach ($customer->getAddresses() as $address) { + $address->setCustomerId($customerId) + ->setRegion($address->getRegion()); + $this->addressRepository->save($address); + if ($address->getId()) { + $savedAddressIds[] = $address->getId(); + } + } + $addressIdsToDelete = array_diff($existingAddressIds, $savedAddressIds); + foreach ($addressIdsToDelete as $addressId) { + $this->addressRepository->deleteById($addressId); + } + } $this->customerRegistry->remove($customerId); $savedCustomer = $this->get($customer->getEmail(), $customer->getWebsiteId()); $this->eventManager->dispatch( diff --git a/app/code/Magento/Customer/etc/db_schema_whitelist.json b/app/code/Magento/Customer/etc/db_schema_whitelist.json index 4aada8f0d81fe..ec7a53945aba3 100644 --- a/app/code/Magento/Customer/etc/db_schema_whitelist.json +++ b/app/code/Magento/Customer/etc/db_schema_whitelist.json @@ -73,7 +73,8 @@ "vat_request_success": true }, "index": { - "CUSTOMER_ADDRESS_ENTITY_PARENT_ID": true + "CUSTOMER_ADDRESS_ENTITY_PARENT_ID": true, + "FTI_BA70344390184AC3F063AB2EB38BC0ED": true }, "constraint": { "PRIMARY": true, diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml index cfc62fc99b10e..28b19d61fbdde 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml @@ -39,25 +39,18 @@ <bookmark name="bookmarks"/> <columnsControls name="columns_controls"/> <!-- Filter Search --> - <filterSearch name="fulltext"> - <argument name="data" xsi:type="array"> - <item name="config" xsi:type="array"> - <item name="provider" xsi:type="string">customer_address_listing.customer_address_listing_data_source</item> - <item name="chipsProvider" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.listing_filters_chips</item> - <item name="storageConfig" xsi:type="array"> - <item name="provider" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.bookmarks</item> - <item name="namespace" xsi:type="string">current.search</item> - </item> - </item> - </argument> + <filterSearch name="fulltext" provider="customer_address_listing.customer_address_listing_data_source"> + <settings> + <chipsProvider>customer_address_listing.customer_address_listing.listing_top.listing_filters_chips</chipsProvider> + <storageConfig> + <param name="provider" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.bookmarks</param> + <param name="namespace" xsi:type="string">current.search</param> + </storageConfig> + </settings> </filterSearch> <filters name="listing_filters"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> - <item name="storageConfig" xsi:type="array"> - <item name="provider" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.bookmarks</item> - <item name="namespace" xsi:type="string">current.filters</item> - </item> <item name="childDefaults" xsi:type="array"> <item name="provider" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.listing_filters</item> <item name="imports" xsi:type="array"> @@ -66,6 +59,12 @@ </item> </item> </argument> + <settings> + <storageConfig> + <param name="provider" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.bookmarks</param> + <param name="namespace" xsi:type="string">current.filters</param> + </storageConfig> + </settings> </filters> <massaction name="listing_massaction" component="Magento_Ui/js/grid/tree-massactions"> <action name="delete"> diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index 6fc8fcac6099f..17d4e7aab5cd3 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -328,10 +328,9 @@ </settings> </component> - <button name="edit_billing_address"> + <button name="edit_billing_address" component="Magento_Customer/js/address/default-address"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> - <item name="component" xsi:type="string">Magento_Customer/js/address/default-address</item> <item name="buttonClasses" xsi:type="string">edit-default-billing-address-button</item> <item name="actions" xsi:type="array"> <item name="0" xsi:type="array"> @@ -375,10 +374,9 @@ </settings> </component> - <button name="edit_shipping_address"> + <button name="edit_shipping_address" component="Magento_Customer/js/address/default-address"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> - <item name="component" xsi:type="string">Magento_Customer/js/address/default-address</item> <item name="buttonClasses" xsi:type="string">edit-default-shipping-address-button</item> <item name="actions" xsi:type="array"> <item name="0" xsi:type="array"> @@ -405,11 +403,10 @@ </imports> </settings> </button> - <button name="add_address"> + <button name="add_address" component="Magento_Customer/js/address/default-address"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="formElement" xsi:type="string">container</item> - <item name="component" xsi:type="string">Magento_Customer/js/address/default-address</item> <item name="buttonClasses" xsi:type="string">add-new-address-button</item> <item name="actions" xsi:type="array"> <item name="0" xsi:type="array"> diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php index c6741f76e0538..5a4a426b58cda 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php @@ -86,9 +86,7 @@ public function testSaveActionWithValidAddressData() $this->objectManager->get(\Magento\Backend\Model\Session::class)->setCustomerFormData($post); $this->customerAddress->execute(); - /** - * Check that errors was generated and set to session - */ + $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); /** @@ -220,4 +218,36 @@ public function testSaveActionWithExistingAdresses() $addresses = $customer->getAddresses(); $this->assertCount(4, $addresses); } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + */ + public function testValidateCustomerWithAddressFailure() + { + $customer = $this->customerRepository->get('customer@example.com'); + $customerId = $customer->getId(); + $post = [ + 'parent_id' => $customerId, + 'firstname' => '', + 'lastname' => '', + 'street' => ['update street'], + 'city' => 'update city', + 'postcode' => '01001', + 'telephone' => '', + ]; + $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); + + $this->objectManager->get(\Magento\Backend\Model\Session::class)->setCustomerFormData($post); + + $this->customerAddress->execute(); + + /** + * Check that errors was generated and set to session + */ + $this->assertSessionMessages( + $this->equalTo(['One or more input exceptions have occurred.']), + \Magento\Framework\Message\MessageInterface::TYPE_ERROR + ); + } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php index 2fe49efd74a6d..292d61c392d06 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/IndexTest.php @@ -122,226 +122,6 @@ public function testSaveActionWithInvalidFormData() $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new')); } - /** - * @magentoDbIsolation enabled - */ - public function testSaveActionWithInvalidCustomerAddressData() - { - $post = [ - 'customer' => [ - 'middlename' => 'test middlename', - 'group_id' => 1, - 'website_id' => 0, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => 'example@domain.com', - 'default_billing' => '_item1', - ], - 'address' => ['_item1' => []], - ]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/save'); - /** - * Check that errors was generated and set to session - */ - $this->assertSessionMessages( - $this->logicalNot($this->isEmpty()), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - /** - * Check that customer data were set to session - */ - $this->assertArraySubset( - $post, - $this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerFormData() - ); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'new')); - } - - /** - * @magentoDbIsolation enabled - */ - public function testSaveActionWithValidCustomerDataAndValidAddressData() - { - $post = [ - 'customer' => [ - 'middlename' => 'test middlename', - 'group_id' => 1, - 'website_id' => 0, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => 'example@domain.com', - 'default_billing' => '_item1', - 'password' => 'password', - ], - 'address' => [ - '_item1' => [ - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'street' => ['test street'], - 'city' => 'test city', - 'region_id' => 10, - 'country_id' => 'US', - 'postcode' => '01001', - 'telephone' => '+7000000001', - 'default_billing' => 'true', - ], - ], - ]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->getRequest()->setParam('back', '1'); - - // Emulate setting customer data to session in editAction - $this->objectManager->get(\Magento\Backend\Model\Session::class)->setCustomerFormData($post); - - $this->dispatch('backend/customer/index/save'); - /** - * Check that errors was generated and set to session - */ - $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); - - /** - * Check that customer data were cleaned after it was saved successfully - */ - $this->assertEmpty($this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerData()); - - /** - * Check that success message is set - */ - $this->assertSessionMessages( - $this->logicalNot($this->isEmpty()), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - - /** - * Check that customer id set and addresses saved - */ - $registry = $this->objectManager->get(\Magento\Framework\Registry::class); - $customerId = $registry->registry(RegistryConstants::CURRENT_CUSTOMER_ID); - $customer = $this->customerRepository->getById($customerId); - $this->assertEquals('test firstname', $customer->getFirstname()); - $addresses = $customer->getAddresses(); - $this->assertEquals(1, count($addresses)); - $this->assertNotEquals(0, $this->accountManagement->getDefaultBillingAddress($customerId)); - $this->assertNull($this->accountManagement->getDefaultShippingAddress($customerId)); - - $urlPatternParts = [ - $this->_baseControllerUrl . 'edit', - 'id/' . $customerId, - 'back/1', - ]; - $urlPattern = '/^' . str_replace('/', '\/', implode('(/.*/)|/', $urlPatternParts)) . '/'; - - $this->assertRedirect( - $this->matchesRegularExpression($urlPattern) - ); - - /** @var \Magento\Newsletter\Model\Subscriber $subscriber */ - $subscriber = $this->objectManager->get(\Magento\Newsletter\Model\SubscriberFactory::class)->create(); - $this->assertEmpty($subscriber->getId()); - $subscriber->loadByCustomerId($customerId); - $this->assertEmpty($subscriber->getId()); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer_sample.php - */ - public function testSaveActionExistingCustomerAndExistingAddressData() - { - $post = [ - 'customer' => [ - 'entity_id' => '1', - 'middlename' => 'test middlename', - 'group_id' => 1, - 'website_id' => 1, - 'firstname' => 'test firstname', - 'lastname' => 'test lastname', - 'email' => 'customer@example.com', - 'new_password' => 'auto', - 'sendemail_store_id' => '1', - 'sendemail' => '1', - 'created_at' => '2000-01-01 00:00:00', - 'default_shipping' => '_item1', - 'default_billing' => 1, - ], - 'address' => [ - '1' => [ - 'firstname' => 'update firstname', - 'lastname' => 'update lastname', - 'street' => ['update street'], - 'city' => 'update city', - 'region_id' => 10, - 'country_id' => 'US', - 'postcode' => '01001', - 'telephone' => '+7000000001', - 'default_billing' => 'true', - ], - '_item1' => [ - 'firstname' => 'new firstname', - 'lastname' => 'new lastname', - 'street' => ['new street'], - 'city' => 'new city', - 'region_id' => 10, - 'country_id' => 'US', - 'postcode' => '01001', - 'telephone' => '+7000000001', - 'default_shipping' => 'true', - ], - '_template_' => [ - 'firstname' => '', - 'lastname' => '', - 'street' => [], - 'city' => '', - 'region_id' => 10, - 'country_id' => 'US', - 'postcode' => '', - 'telephone' => '', - ], - ], - 'subscription' => '', - ]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - $this->getRequest()->setParam('id', 1); - $this->dispatch('backend/customer/index/save'); - - /** Check that success message is set */ - $this->assertSessionMessages( - $this->equalTo(['You saved the customer.']), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - - /** Check that customer id set and addresses saved */ - $registry = $this->objectManager->get(\Magento\Framework\Registry::class); - $customerId = $registry->registry(RegistryConstants::CURRENT_CUSTOMER_ID); - $customer = $this->customerRepository->getById($customerId); - $this->assertEquals('test firstname', $customer->getFirstname()); - - /** - * Addresses should be removed by - * \Magento\Customer\Model\ResourceModel\Customer::_saveAddresses during _afterSave - * addressOne - updated - * addressTwo - removed - * addressThree - removed - * _item1 - new address - */ - $addresses = $customer->getAddresses(); - $this->assertEquals(2, count($addresses)); - $updatedAddress = $this->addressRepository->getById(1); - $this->assertEquals('update firstname', $updatedAddress->getFirstname()); - $this->assertTrue($updatedAddress->isDefaultBilling()); - $this->assertEquals($updatedAddress->getId(), $customer->getDefaultBilling()); - $newAddress = $this->accountManagement->getDefaultShippingAddress($customerId); - $this->assertEquals('new firstname', $newAddress->getFirstname()); - - /** @var \Magento\Newsletter\Model\Subscriber $subscriber */ - $subscriber = $this->objectManager->get(\Magento\Newsletter\Model\SubscriberFactory::class)->create(); - $this->assertEmpty($subscriber->getId()); - $subscriber->loadByCustomerId($customerId); - $this->assertNotEmpty($subscriber->getId()); - $this->assertEquals(1, $subscriber->getStatus()); - $this->assertRedirect($this->stringStartsWith($this->_baseControllerUrl . 'index/key/')); - } - /** * @magentoDataFixture Magento/Newsletter/_files/subscribers.php */ @@ -544,7 +324,7 @@ public function testNewAction() /** * Test the editing of a new customer that has not been saved but the page has been reloaded */ - public function testNewActionWithCustomerData() + public function te1stNewActionWithCustomerData() { $customerData = [ 'customer_id' => 0, @@ -679,59 +459,6 @@ public function testValidateCustomerWithAddressSuccess() $this->assertEquals('{"error":0}', $body); } - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/Customer/_files/customer_address.php - */ - public function testValidateCustomerWithAddressFailure() - { - $customerData = [ - 'customer' => [ - 'entity_id' => '1', - 'middlename' => 'new middlename', - 'group_id' => 1, - 'website_id' => 1, - 'firstname' => '', - 'lastname' => '', - 'email' => '*', - 'default_shipping' => '_item1', - 'new_password' => 'auto', - 'sendemail_store_id' => '1', - 'sendemail' => '1', - ], - 'address' => [ - '1' => [ - 'firstname' => '', - 'lastname' => '', - 'street' => ['update street'], - 'city' => 'update city', - 'postcode' => '01001', - 'telephone' => '', - ], - '_template_' => [ - 'lastname' => '', - 'street' => [], - 'city' => '', - 'country_id' => 'US', - 'postcode' => '', - 'telephone' => '', - ], - ], - ]; - /** - * set customer data - */ - $this->getRequest()->setPostValue($customerData)->setMethod(HttpRequest::METHOD_POST); - $this->dispatch('backend/customer/index/validate'); - $body = $this->getResponse()->getBody(); - - $this->assertContains('{"error":true,"messages":', $body); - $this->assertContains('\"First Name\" is a required value', $body); - $this->assertContains('\"Last Name\" is a required value.', $body); - $this->assertContains('\"Country\" is a required value.', $body); - $this->assertContains('\"Phone Number\" is a required value.', $body); - } - /** * @magentoDbIsolation enabled */ From c2100db5bd2ec177f0fdf5a40ff9ac97e46c0b1a Mon Sep 17 00:00:00 2001 From: Giel Berkers <giel.berkers@isaac.nl> Date: Tue, 6 Nov 2018 11:15:58 +0100 Subject: [PATCH 102/704] [REFACTOR] Split the method to lower cyclomatic complexity --- .../Product/CategoryLinkTest.php | 100 ++++++++++-------- 1 file changed, 54 insertions(+), 46 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CategoryLinkTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CategoryLinkTest.php index c0c061ab8a704..5a1a5906ec4b9 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CategoryLinkTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/CategoryLinkTest.php @@ -134,53 +134,8 @@ public function testSaveCategoryLinks($newCategoryLinks, $dbCategoryLinks, $affe foreach ($affectedIds as $type => $ids) { $expectedResult = array_merge($expectedResult, $ids); - // Verify if the correct insert, update and/or delete actions are performed: - switch ($type) { - case 'insert': - $this->connectionMock - ->expects($this->exactly(empty($ids) ? 0 : 1)) - ->method('insertArray') - ->with( - $this->anything(), - $this->anything(), - $this->callback(function ($data) use ($ids) { - $foundIds = []; - foreach ($data as $row) { - $foundIds[] = $row['category_id']; - } - return $ids === $foundIds; - }) - ); - break; - case 'update': - $this->connectionMock - ->expects($this->exactly(empty($ids) ? 0 : 1)) - ->method('insertOnDuplicate') - ->with( - $this->anything(), - $this->callback(function ($data) use ($ids) { - $foundIds = []; - foreach ($data as $row) { - $foundIds[] = $row['category_id']; - } - return $ids === $foundIds; - }) - ); - break; - case 'delete': - $this->connectionMock - ->expects($this->exactly(empty($ids) ? 0 : 1)) - ->method('delete') - // Verify that the correct category ID's are touched: - ->with( - $this->anything(), - $this->callback(function ($data) use ($ids) { - return array_values($data)[1] === $ids; - }) - ); - break; - } + $this->setupExpectationsForConnection($type, $ids); } $actualResult = $this->model->saveCategoryLinks($product, $newCategoryLinks); @@ -273,4 +228,57 @@ public function getCategoryLinksDataProvider() ] ]; } + + /** + * @param $type + * @param $ids + */ + private function setupExpectationsForConnection($type, $ids): void + { + switch ($type) { + case 'insert': + $this->connectionMock + ->expects($this->exactly(empty($ids) ? 0 : 1)) + ->method('insertArray') + ->with( + $this->anything(), + $this->anything(), + $this->callback(function ($data) use ($ids) { + $foundIds = []; + foreach ($data as $row) { + $foundIds[] = $row['category_id']; + } + return $ids === $foundIds; + }) + ); + break; + case 'update': + $this->connectionMock + ->expects($this->exactly(empty($ids) ? 0 : 1)) + ->method('insertOnDuplicate') + ->with( + $this->anything(), + $this->callback(function ($data) use ($ids) { + $foundIds = []; + foreach ($data as $row) { + $foundIds[] = $row['category_id']; + } + return $ids === $foundIds; + }) + ); + break; + case 'delete': + $this->connectionMock + ->expects($this->exactly(empty($ids) ? 0 : 1)) + ->method('delete') + // Verify that the correct category ID's are touched: + ->with( + $this->anything(), + $this->callback(function ($data) use ($ids) { + return array_values($data)[1] === $ids; + }) + ); + break; + } + } } From 35112c4015f6912dd29c7b65c83ee1aef9fe04b8 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov <lpoluyanov@magento.com> Date: Tue, 6 Nov 2018 12:30:50 +0200 Subject: [PATCH 103/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - UI component declaration fixes --- .../ui_component/customer_address_listing.xml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml index 28b19d61fbdde..6c97a6da5e9e2 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml @@ -49,21 +49,17 @@ </settings> </filterSearch> <filters name="listing_filters"> - <argument name="data" xsi:type="array"> - <item name="config" xsi:type="array"> - <item name="childDefaults" xsi:type="array"> - <item name="provider" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.listing_filters</item> - <item name="imports" xsi:type="array"> - <item name="visible" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.bookmarks:current.columns.${ $.index }.visible</item> - </item> - </item> - </item> - </argument> <settings> <storageConfig> <param name="provider" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.bookmarks</param> <param name="namespace" xsi:type="string">current.filters</param> </storageConfig> + <childDefaults> + <param name="provider" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.listing_filters</param> + <param name="imports" xsi:type="array"> + <item name="visible" xsi:type="string">customer_address_listing.customer_address_listing.listing_top.bookmarks:current.columns.${ $.index }.visible</item> + </param> + </childDefaults> </settings> </filters> <massaction name="listing_massaction" component="Magento_Ui/js/grid/tree-massactions"> From b2ce545d24004e407fa8d83ee3c76b1157ea3212 Mon Sep 17 00:00:00 2001 From: Sona Sargsyan <sona_sargsyan@epam.com> Date: Mon, 5 Nov 2018 15:51:00 +0400 Subject: [PATCH 104/704] MAGETWO-95803: Apply coupon code to guests that create accounts after checking out - Add Automated test --- .../Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml | 1 + .../Test/Mftf/Section/AdminCartPriceRulesFormSection.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml index 34819f641cbc9..bc65f8a2c0816 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml @@ -16,6 +16,7 @@ <element name="orderLink" type="text" selector="a[href*=order_id].order-number" timeout="30"/> <element name="orderNumberText" type="text" selector=".checkout-success > p:nth-child(1)"/> <element name="continueShoppingButton" type="button" selector=".action.primary.continue" timeout="30"/> + <element name="createAnAccount" type="button" selector="input[value='Create an Account']" timeout="30"/> <element name="printLink" type="button" selector=".print" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml index 54f7aa97053cb..2b1845e84fe5b 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Section/AdminCartPriceRulesFormSection.xml @@ -39,6 +39,7 @@ <element name="applyDiscountToShippingLabel" type="checkbox" selector="input[name='apply_to_shipping']+label"/> <element name="discountAmount" type="input" selector="input[name='discount_amount']"/> <element name="discountStep" type="input" selector="input[name='discount_step']"/> + <element name="addRewardPoints" type="input" selector="input[name='extension_attributes[reward_points_delta]']"/> <element name="freeShipping" type="select" selector="//select[@name='simple_free_shipping']"/> <!-- Manage Coupon Codes sub-form --> From 9861eea014c352eb28d76a886db246c4aeaa6bee Mon Sep 17 00:00:00 2001 From: Vital_Pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Tue, 6 Nov 2018 17:33:30 +0300 Subject: [PATCH 105/704] MAGETWO-70379: UI components Date filter field has non-localized date format - Changed the logic of date formatting --- .../Ui/Component/Listing/Columns/Date.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/app/code/Magento/Ui/Component/Listing/Columns/Date.php b/app/code/Magento/Ui/Component/Listing/Columns/Date.php index 1c9916a941458..50cebf1bf6016 100644 --- a/app/code/Magento/Ui/Component/Listing/Columns/Date.php +++ b/app/code/Magento/Ui/Component/Listing/Columns/Date.php @@ -47,6 +47,27 @@ public function __construct( parent::__construct($context, $uiComponentFactory, $components, $data); } + /** + * @inheritdoc + */ + public function prepare() + { + $config = $this->getData('config'); + $config['filter'] = [ + 'filterType' => 'dateRange', + 'templates' => [ + 'date' => [ + 'options' => [ + 'dateFormat' => $this->timezone->getDateFormatWithLongYear() + ] + ] + ] + ]; + $this->setData('config', $config); + + parent::prepare(); + } + /** * @inheritdoc */ From a34906d3bcbf7f12afe9f8a9bd5e750c9bc81bef Mon Sep 17 00:00:00 2001 From: Vital_Pantsialeyeu <vital_pantsialeyeu@epam.com> Date: Tue, 6 Nov 2018 17:47:16 +0300 Subject: [PATCH 106/704] MAGETWO-70379: UI components Date filter field has non-localized date format - Fixed static tests --- app/code/Magento/Ui/Component/Listing/Columns/Date.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Ui/Component/Listing/Columns/Date.php b/app/code/Magento/Ui/Component/Listing/Columns/Date.php index 50cebf1bf6016..ad876b66b6038 100644 --- a/app/code/Magento/Ui/Component/Listing/Columns/Date.php +++ b/app/code/Magento/Ui/Component/Listing/Columns/Date.php @@ -11,6 +11,8 @@ use Magento\Framework\Stdlib\DateTime\TimezoneInterface; /** + * Date format column + * * @api * @since 100.0.2 */ From 425514bcf49e3adb2d0516d70d27dedf11b65345 Mon Sep 17 00:00:00 2001 From: Oleksii Gorbulin <a.gorbulin@ism-ukraine.com> Date: Wed, 7 Nov 2018 00:52:46 +0200 Subject: [PATCH 107/704] 19085-Translation-in-tier-price-phtml-not-working #19085:Translation in tier_price.phtml not working --- .../view/base/templates/product/price/tier_price.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/tier_price.phtml b/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/tier_price.phtml index 18f96cfaaf398..3d143073850b1 100644 --- a/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/tier_price.phtml +++ b/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/tier_price.phtml @@ -15,9 +15,9 @@ + '</span>' + '</span>'; %> <li class="item"> - <%= $t('Buy %1 for %2 each and').replace('%1', item.qty).replace('%2', priceStr) %> + <%= $t('<?= $block->escapeHtml(__('Buy %1 for %2 each and')) ?>').replace('%1', item.qty).replace('%2', priceStr) %> <strong class="benefit"> - <%= $t('save') %><span class="percent tier-<%= key %>"> <%= item.percentage %></span>% + <?= $block->escapeHtml(__('save')) ?><span class="percent tier-<%= key %>"> <%= item.percentage %></span>% </strong> </li> <% }); %> From 29640f15077b1b6e75829054037b96703e5964a8 Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Wed, 7 Nov 2018 13:06:46 +0200 Subject: [PATCH 108/704] Comparing the current block path with the current url path --- .../View/Element/Html/Link/Current.php | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php b/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php index 43bfd46c1193a..cb29c67a2ca30 100644 --- a/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php +++ b/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php @@ -5,6 +5,10 @@ */ namespace Magento\Framework\View\Element\Html\Link; +use Magento\Framework\App\DefaultPathInterface; +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Element\Template\Context; + /** * Block representing link with two possible states. * "Current" state means link leads to URL equivalent to URL of currently displayed page. @@ -17,25 +21,25 @@ * @method null|bool getCurrent() * @method \Magento\Framework\View\Element\Html\Link\Current setCurrent(bool $value) */ -class Current extends \Magento\Framework\View\Element\Template +class Current extends Template { /** * Default path * - * @var \Magento\Framework\App\DefaultPathInterface + * @var DefaultPathInterface */ protected $_defaultPath; /** * Constructor * - * @param \Magento\Framework\View\Element\Template\Context $context - * @param \Magento\Framework\App\DefaultPathInterface $defaultPath + * @param Context $context + * @param DefaultPathInterface $defaultPath * @param array $data */ public function __construct( - \Magento\Framework\View\Element\Template\Context $context, - \Magento\Framework\App\DefaultPathInterface $defaultPath, + Context $context, + DefaultPathInterface $defaultPath, array $data = [] ) { parent::__construct($context, $data); @@ -81,7 +85,7 @@ private function getMca() */ public function isCurrent() { - return $this->getCurrent() || $this->getUrl($this->getPath()) == $this->getUrl($this->getMca()); + return $this->getCurrent() || $this->getUrl($this->getPath()) == $this->getUrl($this->getPathInfo()); } /** @@ -147,4 +151,14 @@ private function getAttributesHtml() return $attributesHtml; } + + /** + * Get current page path info + * + * @return string + */ + private function getPathInfo() + { + return trim($this->_request->getPathInfo(), '/'); + } } From b545997c49beec86291b4a28741c43d01c53c41e Mon Sep 17 00:00:00 2001 From: Stas Kozar <stas.kozar@transoftgroup.com> Date: Wed, 7 Nov 2018 15:14:20 +0200 Subject: [PATCH 109/704] magento/magento2#18990: Shipping address is not validated in checkout when proceeding step as logged in user with default shipping address --- .../view/frontend/web/js/view/shipping.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js index 6ac805f72f78d..c811d3a1e8369 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js @@ -266,9 +266,8 @@ define([ loginFormSelector = 'form[data-role=email-with-possible-login]', emailValidationResult = customer.isLoggedIn(), field, - countryIndexedOptions = registry.get( - this.parentName + '.shippingAddress.shipping-address-fieldset.country_id' - ).indexedOptions, + country = registry.get(this.parentName + '.shippingAddress.shipping-address-fieldset.country_id'), + countryIndexedOptions = country.indexedOptions, option = countryIndexedOptions[quote.shippingAddress().countryId], messageContainer = registry.get('checkout.errors').messageContainer; @@ -323,12 +322,14 @@ define([ shippingAddress['save_in_address_book'] = 1; } selectShippingAddress(shippingAddress); - } else if (customer.isLoggedIn() - && option - && option['is_region_required'] - && !quote.shippingAddress().region + } else if (customer.isLoggedIn() && + option && + option['is_region_required'] && + !quote.shippingAddress().region ) { - messageContainer.addErrorMessage({message: $t('Please specify a regionId in shipping address.')}); + messageContainer.addErrorMessage({ + message: $t('Please specify a regionId in shipping address.') + }); return false; } From 965de969e224387fa307c7f8c099a25d78c4f5a4 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Wed, 7 Nov 2018 16:48:13 +0200 Subject: [PATCH 110/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Add json response to save address action; --- .../Controller/Adminhtml/Address/Save.php | 69 ++++++++++++------- .../Customer/Model/Address/DataProvider.php | 27 ++++---- .../web/js/address/default-address.js | 16 ++--- 3 files changed, 65 insertions(+), 47 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php index 8b70263d3f95b..3bd49d038588c 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php @@ -8,7 +8,10 @@ use Magento\Backend\App\Action; use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Framework\Controller\Result\Redirect; +use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; use Psr\Log\LoggerInterface; /** @@ -53,6 +56,11 @@ class Save extends Action implements HttpPostActionInterface */ private $logger; + /** + * @var JsonFactory + */ + private $resultJsonFactory; + /** * @param Action\Context $context * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository @@ -61,6 +69,7 @@ class Save extends Action implements HttpPostActionInterface * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper * @param \Magento\Customer\Api\Data\AddressInterfaceFactory $addressDataFactory * @param LoggerInterface $logger + * @param JsonFactory $resultJsonFactory */ public function __construct( Action\Context $context, @@ -69,7 +78,8 @@ public function __construct( \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository, \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, \Magento\Customer\Api\Data\AddressInterfaceFactory $addressDataFactory, - LoggerInterface $logger + LoggerInterface $logger, + JsonFactory $resultJsonFactory ) { parent::__construct($context); $this->addressRepository = $addressRepository; @@ -78,23 +88,24 @@ public function __construct( $this->dataObjectHelper = $dataObjectHelper; $this->addressDataFactory = $addressDataFactory; $this->logger = $logger; + $this->resultJsonFactory = $resultJsonFactory; } /** * Save customer address action * - * @return \Magento\Framework\Controller\Result\Redirect - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @return ResultInterface */ - public function execute(): Redirect + public function execute(): ResultInterface { $customerId = $this->getRequest()->getParam('parent_id', false); $addressId = $this->getRequest()->getParam('entity_id', false); - /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ - $customer = $this->customerRepository->getById($customerId); + $error = false; try { + /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */ + $customer = $this->customerRepository->getById($customerId); + $addressForm = $this->formFactory->create( 'customer_address', 'adminhtml_customer_address', @@ -124,29 +135,39 @@ public function execute(): Redirect ); if ($addressId) { $addressToSave->setId($addressId); - $saveMessage = __('Customer address has been updated.'); + $message = __('Customer address has been updated.'); } else { $addressToSave->setId(null); - $saveMessage = __('New customer address has been added.'); + $message = __('New customer address has been added.'); } - - $this->addressRepository->save($addressToSave); - $this->messageManager->addSuccessMessage($saveMessage); - } catch (\Magento\Framework\Exception\LocalizedException $e) { - $this->messageManager->addErrorMessage($e->getMessage()); + $savedAddress = $this->addressRepository->save($addressToSave); + $addressId = $savedAddress->getId(); + } catch (NoSuchEntityException $e) { + $this->logger->critical($e); + $error = true; + $message = __('There is no customer with such id.'); + } catch (LocalizedException $e) { + $error = true; + $message = __($e->getMessage()); $this->logger->critical($e); } catch (\Exception $e) { - $this->messageManager->addExceptionMessage( - $e, - __('We can\'t change customer address right now.') - ); + $error = true; + $message = __('We can\'t change customer address right now.'); + $this->logger->critical($e); } - $resultRedirect = $this->resultRedirectFactory->create(); - $resultRedirect->setPath( - 'customer/index/edit', - ['id' => $customerId, '_current' => true] + $addressId = empty($addressId) ? null : $addressId; + $resultJson = $this->resultJsonFactory->create(); + $resultJson->setData( + [ + 'message' => $message, + 'error' => $error, + 'data' => [ + 'addressId' => $addressId + ] + ] ); - return $resultRedirect; + + return $resultJson; } } diff --git a/app/code/Magento/Customer/Model/Address/DataProvider.php b/app/code/Magento/Customer/Model/Address/DataProvider.php index 1b4bcf94fec15..a7c0da08324d3 100644 --- a/app/code/Magento/Customer/Model/Address/DataProvider.php +++ b/app/code/Magento/Customer/Model/Address/DataProvider.php @@ -126,21 +126,18 @@ public function getData(): array if (null !== $this->loadedData) { return $this->loadedData; } - $items = $this->collection->getItems(); - /** @var Address $item */ - foreach ($items as $item) { - $addressId = $item->getEntityId(); - $item->load($addressId); - $this->loadedData[$addressId] = $item->getData(); - $customerId = $this->loadedData[$addressId]['parent_id']; - /** @var \Magento\Customer\Model\Customer $customer */ - $customer = $this->customerRepository->getById($customerId); - $defaultBilling = $customer->getDefaultBilling(); - $defaultShipping = $customer->getDefaultShipping(); - $this->prepareAddressData($addressId, $this->loadedData, $defaultBilling, $defaultShipping); - - $this->fileUploaderDataResolver->overrideFileUploaderData($item, $this->loadedData[$addressId]); - } + /** @var Address $address */ + $address = $this->collection->getFirstItem(); + $addressId = $address->getEntityId(); + $address->load($addressId); + $this->loadedData[$addressId] = $address->getData(); + $customerId = $this->loadedData[$addressId]['parent_id']; + /** @var \Magento\Customer\Model\Customer $customer */ + $customer = $this->customerRepository->getById($customerId); + $defaultBilling = $customer->getDefaultBilling(); + $defaultShipping = $customer->getDefaultShipping(); + $this->prepareAddressData($addressId, $this->loadedData, $defaultBilling, $defaultShipping); + $this->fileUploaderDataResolver->overrideFileUploaderData($address, $this->loadedData[$addressId]); if (null === $this->loadedData) { $this->loadedData[''] = $this->getDefaultData(); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js index 530df8544c841..b4ec734cb4033 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js @@ -10,8 +10,8 @@ define([ return Button.extend({ defaults: { - entityId: null, - parentId: null + entity_id: null, + parent_id: null }, /** @@ -21,8 +21,8 @@ define([ */ initialize: function () { this._super(); - if (!this.parentId) { - this.visible(this.entityId); + if (!this.parent_id) { + this.visible(this.entity_id); } return this; @@ -36,12 +36,12 @@ define([ */ applyAction: function (action) { if (action.params && action.params[0]) { - action.params[0].entityId = this.entityId; - action.params[0].parentId = this.parentId; + action.params[0].entity_id = this.entity_id; + action.params[0].parent_id = this.parent_id; } else { action.params = [{ - entityId: this.entityId, - parentId: this.parentId + entity_id: this.entity_id, + parent_id: this.parent_id }]; } From 26dc0c33fe4cd0289615676a7381764aeddcaf2b Mon Sep 17 00:00:00 2001 From: BlackEagle <ike.devolder@gmail.com> Date: Wed, 7 Nov 2018 15:21:44 +0100 Subject: [PATCH 111/704] fix cipherMethod detection for openssl 1.1.1 if openssl is updated to 1.1.1, openssl_get_cipher_methods returns all ciphers lowercase. Before you would get mixed uppercase and lowercase ciphers. You can still use uppercase or lowercase to use openssl. To avoid "Not valid cipher method." we must search if the cipherMethod exist in uppercase or lowercase before failing. output of openssl_get_cipher_methods with openssl 1.1.0f: ``` php -r 'print_r(openssl_get_cipher_methods());' Array ( [0] => AES-128-CBC [1] => AES-128-CBC-HMAC-SHA1 [2] => AES-128-CBC-HMAC-SHA256 [3] => AES-128-CFB [4] => AES-128-CFB1 [5] => AES-128-CFB8 [6] => AES-128-CTR [7] => AES-128-ECB [8] => AES-128-OCB [9] => AES-128-OFB [10] => AES-128-XTS [11] => AES-192-CBC [12] => AES-192-CFB [13] => AES-192-CFB1 [14] => AES-192-CFB8 [15] => AES-192-CTR [16] => AES-192-ECB [17] => AES-192-OCB [18] => AES-192-OFB [19] => AES-256-CBC [20] => AES-256-CBC-HMAC-SHA1 [21] => AES-256-CBC-HMAC-SHA256 [22] => AES-256-CFB [23] => AES-256-CFB1 [24] => AES-256-CFB8 [25] => AES-256-CTR [26] => AES-256-ECB [27] => AES-256-OCB [28] => AES-256-OFB [29] => AES-256-XTS [30] => BF-CBC [31] => BF-CFB [32] => BF-ECB [33] => BF-OFB [34] => CAMELLIA-128-CBC [35] => CAMELLIA-128-CFB [36] => CAMELLIA-128-CFB1 [37] => CAMELLIA-128-CFB8 [38] => CAMELLIA-128-CTR [39] => CAMELLIA-128-ECB [40] => CAMELLIA-128-OFB [41] => CAMELLIA-192-CBC [42] => CAMELLIA-192-CFB [43] => CAMELLIA-192-CFB1 [44] => CAMELLIA-192-CFB8 [45] => CAMELLIA-192-CTR [46] => CAMELLIA-192-ECB [47] => CAMELLIA-192-OFB [48] => CAMELLIA-256-CBC [49] => CAMELLIA-256-CFB [50] => CAMELLIA-256-CFB1 [51] => CAMELLIA-256-CFB8 [52] => CAMELLIA-256-CTR [53] => CAMELLIA-256-ECB [54] => CAMELLIA-256-OFB [55] => CAST5-CBC [56] => CAST5-CFB [57] => CAST5-ECB [58] => CAST5-OFB [59] => ChaCha20 [60] => ChaCha20-Poly1305 [61] => DES-CBC [62] => DES-CFB [63] => DES-CFB1 [64] => DES-CFB8 [65] => DES-ECB [66] => DES-EDE [67] => DES-EDE-CBC [68] => DES-EDE-CFB [69] => DES-EDE-OFB [70] => DES-EDE3 [71] => DES-EDE3-CBC [72] => DES-EDE3-CFB [73] => DES-EDE3-CFB1 [74] => DES-EDE3-CFB8 [75] => DES-EDE3-OFB [76] => DES-OFB [77] => DESX-CBC [78] => RC2-40-CBC [79] => RC2-64-CBC [80] => RC2-CBC [81] => RC2-CFB [82] => RC2-ECB [83] => RC2-OFB [84] => RC4 [85] => RC4-40 [86] => RC4-HMAC-MD5 [87] => SEED-CBC [88] => SEED-CFB [89] => SEED-ECB [90] => SEED-OFB [91] => aes-128-cbc [92] => aes-128-cbc-hmac-sha1 [93] => aes-128-cbc-hmac-sha256 [94] => aes-128-ccm [95] => aes-128-cfb [96] => aes-128-cfb1 [97] => aes-128-cfb8 [98] => aes-128-ctr [99] => aes-128-ecb [100] => aes-128-gcm [101] => aes-128-ocb [102] => aes-128-ofb [103] => aes-128-xts [104] => aes-192-cbc [105] => aes-192-ccm [106] => aes-192-cfb [107] => aes-192-cfb1 [108] => aes-192-cfb8 [109] => aes-192-ctr [110] => aes-192-ecb [111] => aes-192-gcm [112] => aes-192-ocb [113] => aes-192-ofb [114] => aes-256-cbc [115] => aes-256-cbc-hmac-sha1 [116] => aes-256-cbc-hmac-sha256 [117] => aes-256-ccm [118] => aes-256-cfb [119] => aes-256-cfb1 [120] => aes-256-cfb8 [121] => aes-256-ctr [122] => aes-256-ecb [123] => aes-256-gcm [124] => aes-256-ocb [125] => aes-256-ofb [126] => aes-256-xts [127] => bf-cbc [128] => bf-cfb [129] => bf-ecb [130] => bf-ofb [131] => camellia-128-cbc [132] => camellia-128-cfb [133] => camellia-128-cfb1 [134] => camellia-128-cfb8 [135] => camellia-128-ctr [136] => camellia-128-ecb [137] => camellia-128-ofb [138] => camellia-192-cbc [139] => camellia-192-cfb [140] => camellia-192-cfb1 [141] => camellia-192-cfb8 [142] => camellia-192-ctr [143] => camellia-192-ecb [144] => camellia-192-ofb [145] => camellia-256-cbc [146] => camellia-256-cfb [147] => camellia-256-cfb1 [148] => camellia-256-cfb8 [149] => camellia-256-ctr [150] => camellia-256-ecb [151] => camellia-256-ofb [152] => cast5-cbc [153] => cast5-cfb [154] => cast5-ecb [155] => cast5-ofb [156] => chacha20 [157] => chacha20-poly1305 [158] => des-cbc [159] => des-cfb [160] => des-cfb1 [161] => des-cfb8 [162] => des-ecb [163] => des-ede [164] => des-ede-cbc [165] => des-ede-cfb [166] => des-ede-ofb [167] => des-ede3 [168] => des-ede3-cbc [169] => des-ede3-cfb [170] => des-ede3-cfb1 [171] => des-ede3-cfb8 [172] => des-ede3-ofb [173] => des-ofb [174] => desx-cbc [175] => id-aes128-CCM [176] => id-aes128-GCM [177] => id-aes128-wrap [178] => id-aes128-wrap-pad [179] => id-aes192-CCM [180] => id-aes192-GCM [181] => id-aes192-wrap [182] => id-aes192-wrap-pad [183] => id-aes256-CCM [184] => id-aes256-GCM [185] => id-aes256-wrap [186] => id-aes256-wrap-pad [187] => id-smime-alg-CMS3DESwrap [188] => rc2-40-cbc [189] => rc2-64-cbc [190] => rc2-cbc [191] => rc2-cfb [192] => rc2-ecb [193] => rc2-ofb [194] => rc4 [195] => rc4-40 [196] => rc4-hmac-md5 [197] => seed-cbc [198] => seed-cfb [199] => seed-ecb [200] => seed-ofb ) ``` that output is missing the uppercase versions using openssl 1.1.1: ``` php -r 'print_r(openssl_get_cipher_methods());' Array ( [0] => aes-128-cbc [1] => aes-128-cbc-hmac-sha1 [2] => aes-128-cbc-hmac-sha256 [3] => aes-128-ccm [4] => aes-128-cfb [5] => aes-128-cfb1 [6] => aes-128-cfb8 [7] => aes-128-ctr [8] => aes-128-ecb [9] => aes-128-gcm [10] => aes-128-ocb [11] => aes-128-ofb [12] => aes-128-xts [13] => aes-192-cbc [14] => aes-192-ccm [15] => aes-192-cfb [16] => aes-192-cfb1 [17] => aes-192-cfb8 [18] => aes-192-ctr [19] => aes-192-ecb [20] => aes-192-gcm [21] => aes-192-ocb [22] => aes-192-ofb [23] => aes-256-cbc [24] => aes-256-cbc-hmac-sha1 [25] => aes-256-cbc-hmac-sha256 [26] => aes-256-ccm [27] => aes-256-cfb [28] => aes-256-cfb1 [29] => aes-256-cfb8 [30] => aes-256-ctr [31] => aes-256-ecb [32] => aes-256-gcm [33] => aes-256-ocb [34] => aes-256-ofb [35] => aes-256-xts [36] => aria-128-cbc [37] => aria-128-ccm [38] => aria-128-cfb [39] => aria-128-cfb1 [40] => aria-128-cfb8 [41] => aria-128-ctr [42] => aria-128-ecb [43] => aria-128-gcm [44] => aria-128-ofb [45] => aria-192-cbc [46] => aria-192-ccm [47] => aria-192-cfb [48] => aria-192-cfb1 [49] => aria-192-cfb8 [50] => aria-192-ctr [51] => aria-192-ecb [52] => aria-192-gcm [53] => aria-192-ofb [54] => aria-256-cbc [55] => aria-256-ccm [56] => aria-256-cfb [57] => aria-256-cfb1 [58] => aria-256-cfb8 [59] => aria-256-ctr [60] => aria-256-ecb [61] => aria-256-gcm [62] => aria-256-ofb [63] => bf-cbc [64] => bf-cfb [65] => bf-ecb [66] => bf-ofb [67] => camellia-128-cbc [68] => camellia-128-cfb [69] => camellia-128-cfb1 [70] => camellia-128-cfb8 [71] => camellia-128-ctr [72] => camellia-128-ecb [73] => camellia-128-ofb [74] => camellia-192-cbc [75] => camellia-192-cfb [76] => camellia-192-cfb1 [77] => camellia-192-cfb8 [78] => camellia-192-ctr [79] => camellia-192-ecb [80] => camellia-192-ofb [81] => camellia-256-cbc [82] => camellia-256-cfb [83] => camellia-256-cfb1 [84] => camellia-256-cfb8 [85] => camellia-256-ctr [86] => camellia-256-ecb [87] => camellia-256-ofb [88] => cast5-cbc [89] => cast5-cfb [90] => cast5-ecb [91] => cast5-ofb [92] => chacha20 [93] => chacha20-poly1305 [94] => des-cbc [95] => des-cfb [96] => des-cfb1 [97] => des-cfb8 [98] => des-ecb [99] => des-ede [100] => des-ede-cbc [101] => des-ede-cfb [102] => des-ede-ofb [103] => des-ede3 [104] => des-ede3-cbc [105] => des-ede3-cfb [106] => des-ede3-cfb1 [107] => des-ede3-cfb8 [108] => des-ede3-ofb [109] => des-ofb [110] => desx-cbc [111] => id-aes128-CCM [112] => id-aes128-GCM [113] => id-aes128-wrap [114] => id-aes128-wrap-pad [115] => id-aes192-CCM [116] => id-aes192-GCM [117] => id-aes192-wrap [118] => id-aes192-wrap-pad [119] => id-aes256-CCM [120] => id-aes256-GCM [121] => id-aes256-wrap [122] => id-aes256-wrap-pad [123] => id-smime-alg-CMS3DESwrap [124] => rc2-40-cbc [125] => rc2-64-cbc [126] => rc2-cbc [127] => rc2-cfb [128] => rc2-ecb [129] => rc2-ofb [130] => rc4 [131] => rc4-40 [132] => rc4-hmac-md5 [133] => seed-cbc [134] => seed-cfb [135] => seed-ecb [136] => seed-ofb [137] => sm4-cbc [138] => sm4-cfb [139] => sm4-ctr [140] => sm4-ecb [141] => sm4-ofb ) ``` So checking if uppercase or lowercase exists in the array fixes the issue with openssl 1.1.1 where the uppercase 'AES-256-CBC' is not found. You could also fix this by changing the $cipherMethod to 'aes-256-cbc', but you never know if openssl will change the output case again. The issue appears when just running the unittests on php with openssl 1.1.1, with the previous version of openssl (1.1.0f) the issue is not yet present. Test: ``` $ openssl version OpenSSL 1.1.1 11 Sep 2018 $ php -d memory_limit=2048M vendor/bin/phpunit --configuration dev/tests/unit/phpunit.xml.dist PHPUnit 6.5.13 by Sebastian Bergmann and contributors. ..................................................................................... 85 / 21418 ( 0%) ..................................................................................... 170 / 21418 ( 0%) ..............EE...F................................................................. 255 / 21418 ( 1%) ..................................................................................... 340 / 21418 ( 1%) ... There were 2 errors: 1) Magento\Analytics\Test\Unit\Model\CryptographerTest::testEncode Magento\Framework\Exception\LocalizedException: The data is invalid. Use a valid cipher method and try again. /phpapp/app/code/Magento/Analytics/Model/Cryptographer.php:70 /phpapp/app/code/Magento/Analytics/Test/Unit/Model/CryptographerTest.php:134 2) Magento\Analytics\Test\Unit\Model\CryptographerTest::testEncodeUniqueInitializationVector Magento\Framework\Exception\LocalizedException: The data is invalid. Use a valid cipher method and try again. /phpapp/app/code/Magento/Analytics/Model/Cryptographer.php:70 /phpapp/app/code/Magento/Analytics/Test/Unit/Model/CryptographerTest.php:167 ... ``` Signed-off-by: BlackEagle <ike.devolder@gmail.com> --- app/code/Magento/Analytics/Model/Cryptographer.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Analytics/Model/Cryptographer.php b/app/code/Magento/Analytics/Model/Cryptographer.php index 665d564814b14..efddcb501aabb 100644 --- a/app/code/Magento/Analytics/Model/Cryptographer.php +++ b/app/code/Magento/Analytics/Model/Cryptographer.php @@ -129,7 +129,12 @@ private function getInitializationVector() */ private function validateCipherMethod($cipherMethod) { - $methods = openssl_get_cipher_methods(); + $methods = array_map( + 'strtolower', + openssl_get_cipher_methods() + ); + $cipherMethod = strtolower($cipherMethod); + return (false !== array_search($cipherMethod, $methods)); } } From 9ca93f97c170b26e0f90be10325cdcc5d296524f Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Thu, 8 Nov 2018 10:37:04 +0200 Subject: [PATCH 112/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Add json response to delete, mass delete address actions; --- .../Controller/Adminhtml/Address/Delete.php | 52 ++++++++++---- .../Adminhtml/Address/MassDelete.php | 70 ++++++++++++++----- .../Controller/Adminhtml/Address/Save.php | 6 +- .../Customer/Model/Address/DataProvider.php | 26 +++---- 4 files changed, 110 insertions(+), 44 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php index 7630dba780357..3caba47961af0 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php @@ -8,14 +8,16 @@ namespace Magento\Customer\Controller\Adminhtml\Address; use Magento\Backend\App\Action; -use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Framework\Controller\Result\Redirect; +use Magento\Framework\App\Action\HttpDeleteActionInterface; +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Controller\Result\JsonFactory; use Magento\Customer\Api\AddressRepositoryInterface; +use Psr\Log\LoggerInterface; /** * Button for deletion of customer address in admin * */ -class Delete extends Action implements HttpPostActionInterface +class Delete extends Action implements HttpDeleteActionInterface { /** * Authorization level of a basic admin session @@ -29,39 +31,65 @@ class Delete extends Action implements HttpPostActionInterface */ private $addressRepository; + /** + * @var JsonFactory + */ + private $resultJsonFactory; + + /** + * @var LoggerInterface + */ + private $logger; + /** * @param Action\Context $context * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository + * @param JsonFactory $resultJsonFactory + * @param LoggerInterface $logger */ public function __construct( Action\Context $context, - AddressRepositoryInterface $addressRepository + AddressRepositoryInterface $addressRepository, + JsonFactory $resultJsonFactory, + LoggerInterface $logger ) { parent::__construct($context); $this->addressRepository = $addressRepository; + $this->resultJsonFactory = $resultJsonFactory; + $this->logger = $logger; } /** * Delete customer address action * - * @return \Magento\Framework\Controller\Result\Redirect + * @return Json * @throws \Magento\Framework\Exception\LocalizedException */ - public function execute(): Redirect + public function execute(): Json { $customerId = $this->getRequest()->getParam('parent_id', false); $addressId = $this->getRequest()->getParam('id', false); + $error = false; + $message = ''; if ($addressId && $this->addressRepository->getById($addressId)->getCustomerId() === $customerId) { try { $this->addressRepository->deleteById($addressId); - $this->messageManager->addSuccessMessage(__('You deleted the address.')); - } catch (\Exception $other) { - $this->messageManager->addExceptionMessage($other, __('We can\'t delete the address right now.')); + $message = __('You deleted the address.'); + } catch (\Exception $e) { + $error = true; + $message = __('We can\'t delete the address right now.'); + $this->logger->critical($e); } } - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ - $resultRedirect = $this->resultRedirectFactory->create(); - return $resultRedirect->setPath('customer/index/edit/id', ['id' => $customerId]); + $resultJson = $this->resultJsonFactory->create(); + $resultJson->setData( + [ + 'message' => $message, + 'error' => $error, + ] + ); + + return $resultJson; } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php index b8df3fdf81599..09523f9e6a956 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php @@ -8,12 +8,15 @@ use Magento\Backend\App\Action; use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\Controller\Result\Json; use Magento\Backend\App\Action\Context; +use Magento\Framework\Controller\Result\JsonFactory; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Ui\Component\MassAction\Filter; use Magento\Customer\Model\ResourceModel\Address\CollectionFactory; use Magento\Customer\Api\AddressRepositoryInterface; -use Magento\Backend\Model\View\Result\Redirect; +use Psr\Log\LoggerInterface; /** * Class to delete selected customer addresses through massaction @@ -38,52 +41,85 @@ class MassDelete extends Action implements HttpPostActionInterface protected $collectionFactory; /** - * @var \Magento\Customer\Api\AddressRepositoryInterface + * @var AddressRepositoryInterface */ private $addressRepository; + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var JsonFactory + */ + private $resultJsonFactory; + /** * @param Context $context * @param Filter $filter * @param CollectionFactory $collectionFactory * @param AddressRepositoryInterface $addressRepository + * @param LoggerInterface $logger + * @param JsonFactory $resultJsonFactory */ public function __construct( Context $context, Filter $filter, CollectionFactory $collectionFactory, - AddressRepositoryInterface $addressRepository + AddressRepositoryInterface $addressRepository, + LoggerInterface $logger, + JsonFactory $resultJsonFactory ) { $this->filter = $filter; $this->collectionFactory = $collectionFactory; $this->addressRepository = $addressRepository; + $this->logger = $logger; + $this->resultJsonFactory = $resultJsonFactory; parent::__construct($context); } /** * Delete specified customer addresses using grid massaction * - * @return Redirect - * @throws \Magento\Framework\Exception\LocalizedException|\Exception + * @return Json + * @throws LocalizedException */ - public function execute(): Redirect + public function execute(): Json { /** @var \Magento\Customer\Model\ResourceModel\Address\Collection $collection */ $collection = $this->filter->getCollection($this->collectionFactory->create()); $collectionSize = $collection->getSize(); + $error = false; - // Get id of the first item from addresses collection for the ResultRedirect and build a correct redirect URL - $customerId = $collection->getFirstItem()->getParentId(); - - /** @var \Magento\Customer\Model\Address $address */ - foreach ($collection as $address) { - $this->addressRepository->deleteById($address->getId()); + try { + /** @var \Magento\Customer\Model\Address $address */ + foreach ($collection as $address) { + $this->addressRepository->deleteById($address->getId()); + } + $message = __('A total of %1 record(s) have been deleted.', $collectionSize); + } catch (NoSuchEntityException $e) { + $message = __('There is no such address entity to delete.'); + $error = true; + $this->logger->critical($e); + } catch (LocalizedException $e) { + $message = __($e->getMessage()); + $error = true; + $this->logger->critical($e); + } catch (\Exception $e) { + $message = __('We can\'t mass delete the addresses right now.'); + $error = true; + $this->logger->critical($e); } - $this->messageManager->addSuccessMessage(__('A total of %1 record(s) have been deleted.', $collectionSize)); - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ - $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); + $resultJson = $this->resultJsonFactory->create(); + $resultJson->setData( + [ + 'message' => $message, + 'error' => $error, + ] + ); - return $resultRedirect->setPath('customer/index/edit/id', ['id' => $customerId]); + return $resultJson; } } diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php index 3bd49d038588c..855b8aedb1f73 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php @@ -8,8 +8,8 @@ use Magento\Backend\App\Action; use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\Result\JsonFactory; -use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Psr\Log\LoggerInterface; @@ -94,9 +94,9 @@ public function __construct( /** * Save customer address action * - * @return ResultInterface + * @return Json */ - public function execute(): ResultInterface + public function execute(): Json { $customerId = $this->getRequest()->getParam('parent_id', false); $addressId = $this->getRequest()->getParam('entity_id', false); diff --git a/app/code/Magento/Customer/Model/Address/DataProvider.php b/app/code/Magento/Customer/Model/Address/DataProvider.php index a7c0da08324d3..c41214b91b8c2 100644 --- a/app/code/Magento/Customer/Model/Address/DataProvider.php +++ b/app/code/Magento/Customer/Model/Address/DataProvider.php @@ -126,18 +126,20 @@ public function getData(): array if (null !== $this->loadedData) { return $this->loadedData; } - /** @var Address $address */ - $address = $this->collection->getFirstItem(); - $addressId = $address->getEntityId(); - $address->load($addressId); - $this->loadedData[$addressId] = $address->getData(); - $customerId = $this->loadedData[$addressId]['parent_id']; - /** @var \Magento\Customer\Model\Customer $customer */ - $customer = $this->customerRepository->getById($customerId); - $defaultBilling = $customer->getDefaultBilling(); - $defaultShipping = $customer->getDefaultShipping(); - $this->prepareAddressData($addressId, $this->loadedData, $defaultBilling, $defaultShipping); - $this->fileUploaderDataResolver->overrideFileUploaderData($address, $this->loadedData[$addressId]); + $items = $this->collection->getItems(); + /** @var Address $item */ + foreach ($items as $item) { + $addressId = $item->getEntityId(); + $item->load($addressId); + $this->loadedData[$addressId] = $item->getData(); + $customerId = $this->loadedData[$addressId]['parent_id']; + /** @var \Magento\Customer\Model\Customer $customer */ + $customer = $this->customerRepository->getById($customerId); + $defaultBilling = $customer->getDefaultBilling(); + $defaultShipping = $customer->getDefaultShipping(); + $this->prepareAddressData($addressId, $this->loadedData, $defaultBilling, $defaultShipping); + $this->fileUploaderDataResolver->overrideFileUploaderData($item, $this->loadedData[$addressId]); + } if (null === $this->loadedData) { $this->loadedData[''] = $this->getDefaultData(); From 14bc369dfce948a564a46178a7c2a413b3b71b1a Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Thu, 8 Nov 2018 11:40:51 +0200 Subject: [PATCH 113/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Add json response to set default shipping, set default billing actions; --- .../Address/AbstractDefaultAddress.php | 53 +++++++++++++------ .../Controller/Adminhtml/Address/Delete.php | 10 ++-- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php index 51e02e9ee096e..9ec38f4929a87 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php @@ -8,15 +8,17 @@ namespace Magento\Customer\Controller\Adminhtml\Address; use Magento\Backend\App\Action; -use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Framework\Controller\Result\Redirect; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Controller\Result\JsonFactory; use Magento\Framework\Phrase; use Psr\Log\LoggerInterface; /** * Abstract class for customer default addresses changing */ -abstract class AbstractDefaultAddress extends Action implements HttpPostActionInterface +abstract class AbstractDefaultAddress extends Action implements HttpGetActionInterface { /** * Authorization level of a basic admin session @@ -26,7 +28,7 @@ abstract class AbstractDefaultAddress extends Action implements HttpPostActionIn public const ADMIN_RESOURCE = 'Magento_Customer::manage'; /** - * @var \Magento\Customer\Api\AddressRepositoryInterface + * @var AddressRepositoryInterface */ private $addressRepository; @@ -35,46 +37,63 @@ abstract class AbstractDefaultAddress extends Action implements HttpPostActionIn */ private $logger; + /** + * @var JsonFactory + */ + private $resultJsonFactory; + /** * @param Action\Context $context - * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository + * @param AddressRepositoryInterface $addressRepository * @param LoggerInterface $logger + * @param JsonFactory $resultJsonFactory */ public function __construct( Action\Context $context, - \Magento\Customer\Api\AddressRepositoryInterface $addressRepository, - LoggerInterface $logger + AddressRepositoryInterface $addressRepository, + LoggerInterface $logger, + JsonFactory $resultJsonFactory ) { parent::__construct($context); $this->addressRepository = $addressRepository; $this->logger = $logger; + $this->resultJsonFactory = $resultJsonFactory; } /** * Execute action to set customer default billing or shipping address * - * @return \Magento\Framework\Controller\Result\Redirect + * @return Json */ - public function execute(): Redirect + public function execute(): Json { $customerId = $this->getRequest()->getParam('parent_id', false); $addressId = $this->getRequest()->getParam('id', false); + $error = false; + $message = ''; + if ($addressId) { try { $address = $this->addressRepository->getById($addressId)->setCustomerId($customerId); $this->setAddressAsDefault($address); $this->addressRepository->save($address); - - $this->messageManager->addSuccessMessage($this->getSuccessMessage()); - } catch (\Exception $other) { - $this->logger->critical($other); - $this->messageManager->addExceptionMessage($other, $this->getExceptionMessage()); + $message = $this->getSuccessMessage(); + } catch (\Exception $e) { + $error = true; + $message = $this->getExceptionMessage(); + $this->logger->critical($e); } } - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ - $resultRedirect = $this->resultRedirectFactory->create(); - return $resultRedirect->setPath('customer/index/edit/id', ['id' => $customerId]); + $resultJson = $this->resultJsonFactory->create(); + $resultJson->setData( + [ + 'message' => $message, + 'error' => $error, + ] + ); + + return $resultJson; } /** diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php index 3caba47961af0..65da3bca49e9b 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php @@ -8,16 +8,16 @@ namespace Magento\Customer\Controller\Adminhtml\Address; use Magento\Backend\App\Action; -use Magento\Framework\App\Action\HttpDeleteActionInterface; +use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\Result\JsonFactory; use Magento\Customer\Api\AddressRepositoryInterface; use Psr\Log\LoggerInterface; /** - * Button for deletion of customer address in admin * + * Button for deletion of customer address in admin */ -class Delete extends Action implements HttpDeleteActionInterface +class Delete extends Action implements HttpGetActionInterface { /** * Authorization level of a basic admin session @@ -27,7 +27,7 @@ class Delete extends Action implements HttpDeleteActionInterface public const ADMIN_RESOURCE = 'Magento_Customer::manage'; /** - * @var \Magento\Customer\Api\AddressRepositoryInterface + * @var AddressRepositoryInterface */ private $addressRepository; @@ -43,7 +43,7 @@ class Delete extends Action implements HttpDeleteActionInterface /** * @param Action\Context $context - * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository + * @param AddressRepositoryInterface $addressRepository * @param JsonFactory $resultJsonFactory * @param LoggerInterface $logger */ From e4f006090981154d5d404dca04f07ecbff54938a Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Thu, 8 Nov 2018 16:43:52 +0200 Subject: [PATCH 114/704] MAGETWO-96178: Customer addresses should be saved using Ajax --- .../Controller/Adminhtml/Address/Save.php | 2 +- .../Listing/Address/Column/Actions.php | 7 +- .../ui_component/customer_address_form.xml | 17 ++++ .../ui_component/customer_address_listing.xml | 31 ++++--- .../web/js/address/default-address.js | 36 +++---- .../web/js/form/components/insert-form.js | 55 +++++++++++ .../web/js/form/components/insert-listing.js | 53 +++++++++++ .../adminhtml/web/js/grid/columns/actions.js | 93 +++++++++++++++++++ .../view/adminhtml/web/js/grid/massactions.js | 92 ++++++++++++++++++ .../web/template/default-address.html | 7 +- .../view/base/ui_component/customer_form.xml | 33 ++++++- 11 files changed, 381 insertions(+), 45 deletions(-) create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-listing.js create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php index 855b8aedb1f73..0bdb0d44d29a9 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Save.php @@ -163,7 +163,7 @@ public function execute(): Json 'message' => $message, 'error' => $error, 'data' => [ - 'addressId' => $addressId + 'entity_id' => $addressId ] ] ); diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php index e44dc7988761f..490a14169e7b7 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php @@ -81,24 +81,26 @@ public function prepareDataSource(array $dataSource): array 'hidden' => false, ]; - $item[$name]['set_default_billing'] = [ + $item[$name]['setDefaultBilling'] = [ 'href' => $this->urlBuilder->getUrl( self::CUSTOMER_ADDRESS_PATH_DEFAULT_BILLING, ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] ), 'label' => __('Set as default billing'), + 'isAjax' => true, 'confirm' => [ 'title' => __('Set address as default billing'), 'message' => __('Are you sure you want to set the address as default billing address?') ] ]; - $item[$name]['set_default_shipping'] = [ + $item[$name]['setDefaultShipping'] = [ 'href' => $this->urlBuilder->getUrl( self::CUSTOMER_ADDRESS_PATH_DEFAULT_SHIPPING, ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] ), 'label' => __('Set as default shipping'), + 'isAjax' => true, 'confirm' => [ 'title' => __('Set address as default shipping'), 'message' => __('Are you sure you want to set the address as default shipping address?') @@ -111,6 +113,7 @@ public function prepareDataSource(array $dataSource): array ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] ), 'label' => __('Delete'), + 'isAjax' => true, 'confirm' => [ 'title' => __('Delete address'), 'message' => __('Are you sure you want to delete the address?') diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index a2258d7380e24..bcc5b86c5e65d 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -21,6 +21,8 @@ <button name="save" class="Magento\Customer\Block\Adminhtml\Edit\Address\SaveButton"/> </buttons> <namespace>customer_address_form</namespace> + <ajaxSave>true</ajaxSave> + <ajaxSaveType>simple</ajaxSaveType> <dataScope>data</dataScope> <deps> <dep>customer_address_form.customer_address_form_data_source</dep> @@ -44,6 +46,21 @@ </settings> </dataProvider> </dataSource> + <container name="messages" component="Magento_Ui/js/form/components/html"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="additionalClasses" xsi:type="string">message message-error</item> + <item name="visible" xsi:type="boolean">false</item> + <item name="imports" xsi:type="array"> + <item name="responseData" xsi:type="string">${ $.parentName }:responseData</item> + </item> + <item name="listens" xsi:type="array"> + <item name="responseData.error" xsi:type="string">visible</item> + <item name="responseData.messages" xsi:type="string">content</item> + </item> + </item> + </argument> + </container> <fieldset name="general"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml index 6c97a6da5e9e2..b48d53f9e8c5d 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml @@ -62,18 +62,23 @@ </childDefaults> </settings> </filters> - <massaction name="listing_massaction" component="Magento_Ui/js/grid/tree-massactions"> - <action name="delete"> - <settings> - <confirm> - <message translate="true">Are you sure to delete selected address?</message> - <title translate="true">Delete items - - - delete - - - + + + + + + true + + Are you sure to delete selected address? + Delete items + + + delete + Delete + + + + @@ -157,7 +162,7 @@ - + entity_id diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js index b4ec734cb4033..2ca75d6149da4 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/address/default-address.js @@ -4,28 +4,18 @@ */ define([ - 'Magento_Ui/js/form/components/button' -], function (Button) { + 'Magento_Ui/js/form/components/button', + 'underscore' +], function (Button, _) { 'use strict'; return Button.extend({ defaults: { - entity_id: null, - parent_id: null - }, - - /** - * Initializes component. - * - * @returns {Button} - */ - initialize: function () { - this._super(); - if (!this.parent_id) { - this.visible(this.entity_id); + entityId: null, + parentId: null, + listens: { + entity: 'changeVisibility' } - - return this; }, /** @@ -36,16 +26,20 @@ define([ */ applyAction: function (action) { if (action.params && action.params[0]) { - action.params[0].entity_id = this.entity_id; - action.params[0].parent_id = this.parent_id; + action.params[0]['entity_id'] = this.entityId; + action.params[0]['parent_id'] = this.parentId; } else { action.params = [{ - entity_id: this.entity_id, - parent_id: this.parent_id + 'entity_id': this.entityId, + 'parent_id': this.parentId }]; } this._super(); + }, + + changeVisibility: function (entity) { + this.visible(!_.isEmpty(entity)); } }); }); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js new file mode 100644 index 0000000000000..25303899894c4 --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js @@ -0,0 +1,55 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'Magento_Ui/js/form/components/insert-form' +], function (Insert) { + 'use strict'; + + return Insert.extend({ + defaults: { + listens: { + responseData: 'onResponse' + }, + modules: { + addressListing: '${ $.addressListingProvider }', + addressModal: '${ $.addressModalProvider }' + } + }, + + onResponse: function (responseData) { + var data; + + if (!responseData.error) { + this.addressModal().closeModal(); + this.addressListing().reload({ + refresh: true + }); + data = this.externalSource().get('data'); + this.saveAddress(responseData, data); + } + }, + + saveAddress: function (responseData, data) { + data['entity_id'] = responseData.data['entity_id']; + + if (parseFloat(data['default_billing'])) { + this.source.set('data.default_billing_address', data); + } else if ( + parseFloat(this.source.get('data.default_billing_address')['entity_id']) === data['entity_id'] + ) { + this.source.set('data.default_billing_address', []); + } + + if (parseFloat(data['default_shipping'])) { + this.source.set('data.default_shipping_address', data); + } else if ( + parseFloat(this.source.get('data.default_shipping_address')['entity_id']) === data['entity_id'] + ) { + this.source.set('data.default_shipping_address', []); + } + } + }); +}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-listing.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-listing.js new file mode 100644 index 0000000000000..0dd828b03b419 --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-listing.js @@ -0,0 +1,53 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'Magento_Ui/js/form/components/insert-listing' +], function (Insert) { + 'use strict'; + + return Insert.extend({ + onAction: function (data) { + this[data.action + 'Action'].call(this, data.data); + }, + onMassAction: function (data) { + this[data.action + 'Massaction'].call(this, data.data); + }, + + setDefaultBillingAction: function (data) { + this.source.set('data.default_billing_address', data); + }, + + setDefaultShippingAction: function (data) { + this.source.set('data.default_shipping_address', data); + }, + + deleteAction: function (data) { + var defaultShippingId = parseFloat(this.source.get('data.default_shipping_address.entity_id')), + defaultBillingId = parseFloat(this.source.get('data.default_billing_address.entity_id')); + + if (parseFloat(data[data['id_field_name']]) === defaultShippingId) { + this.source.set('data.default_shipping_address', []); + } + if (parseFloat(data[data['id_field_name']]) === defaultBillingId) { + this.source.set('data.default_billing_address', []); + } + }, + + //TODO: release logic with massaction + deleteMassaction: function (data) { + debugger; + // var defaultShippingId = parseFloat(this.source.get('data.default_shipping_address.entity_id')), + // defaultBillingId = parseFloat(this.source.get('data.default_billing_address.entity_id')); + // + // if (parseFloat(data[data['id_field_name']]) === defaultShippingId) { + // this.source.set('data.default_shipping_address', []); + // } + // if (parseFloat(data[data['id_field_name']]) === defaultBillingId) { + // this.source.set('data.default_billing_address', []); + // } + } + }); +}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js b/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js new file mode 100644 index 0000000000000..43ab06c8014ee --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js @@ -0,0 +1,93 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * @api + */ +define([ + 'Magento_Ui/js/grid/columns/actions', + 'Magento_Ui/js/modal/alert', + 'underscore', + 'jquery', + 'mage/translate' +], function (Actions, uiAlert, _, $, $t) { + 'use strict'; + + return Actions.extend({ + defaults: { + ajaxSettings: { + method: 'GET', + dataType: 'json' + }, + listens: { + action: 'onAction' + } + }, + + onAction: function (data) { + if (data.action === 'delete') { + this.source().reload({ + refresh: true + }); + } + }, + + /** + * Default action callback. Redirects to + * the specified in action's data url. + * + * @param {String} actionIndex - Action's identifier. + * @param {(Number|String)} recordId - Id of the record associated + * with a specified action. + * @param {Object} action - Action's data. + */ + defaultCallback: function (actionIndex, recordId, action) { + if (action.isAjax) { + this.request(action.href).done(function (response) { + var data; + + if (!response.error) { + data = _.findWhere(this.rows, { + _rowIndex: action.rowIndex + }); + + this.trigger('action', { + action: actionIndex, + data: data + }); + } + }.bind(this)); + + } else { + this._super(); + } + }, + + request: function (href) { + var settings = _.extend({}, this.ajaxSettings, { + url: href + }); + + $('body').trigger('processStart'); + + return $.ajax(settings) + .done(function (response) { + if (response.error) { + uiAlert({ + content: response.message + }); + } + }) + .fail(function () { + uiAlert({ + content: $t('Sorry, there has been an error processing your request. Please try again later.') + }); + }) + .always(function () { + $('body').trigger('processStop'); + }); + } + }); +}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js b/app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js new file mode 100644 index 0000000000000..1aefa2e249958 --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js @@ -0,0 +1,92 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * @api + */ +define([ + 'Magento_Ui/js/grid/massactions', + 'Magento_Ui/js/modal/alert', + 'underscore', + 'jquery', + 'mage/translate' +], function (Massactions, uiAlert, _, $, $t) { + 'use strict'; + + return Massactions.extend({ + defaults: { + ajaxSettings: { + method: 'POST', + dataType: 'json' + }, + listens: { + massaction: 'onAction' + } + }, + + onAction: function (data) { + if (data.action === 'delete') { + this.source.reload({ + refresh: true + }); + } + }, + + defaultCallback: function (action, data) { + var itemsType, selections; + + if (action.isAjax) { + itemsType = data.excludeMode ? 'excluded' : 'selected'; + selections = {}; + + selections[itemsType] = data[itemsType]; + + if (!selections[itemsType].length) { + selections[itemsType] = false; + } + + _.extend(selections, data.params || {}); + + this.request(action.url, selections).done(function (response) { + if (!response.error) { + this.trigger('massaction', { + action: action.type, + data: selections + }); + } + }.bind(this)); + + } else { + this._super(); + } + }, + + request: function (href, data) { + var settings = _.extend({}, this.ajaxSettings, { + url: href, + data: data + }); + + $('body').trigger('processStart'); + + return $.ajax(settings) + .done(function (response) { + if (response.error) { + uiAlert({ + content: response.message + }); + } + }) + .fail(function () { + uiAlert({ + content: $t('Sorry, there has been an error processing your request. Please try again later.') + }); + }) + .always(function () { + $('body').trigger('processStop'); + }); + } + }); +}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html index 1ee5a4da81d2b..96158e9921e22 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html +++ b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html @@ -4,7 +4,7 @@
-
+
@@ -16,7 +16,8 @@
- + +
@@ -36,7 +37,7 @@
VAT:
- +
diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index 17d4e7aab5cd3..d6d61c892e00d 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -319,6 +319,9 @@ Default Billing Address customer-default-billing-address-content The customer does not have default billing address + + true +
@@ -353,7 +356,8 @@ Edit true - ${ $.provider}:data.default_billing_address.entity_id + ${ $.provider}:data.default_billing_address.entity_id + ${ $.provider}:data.default_billing_address @@ -365,6 +369,9 @@ Default Shipping Address customer-default-shipping-address-content The customer does not have default shipping address + + true + @@ -399,7 +406,8 @@ Edit true - ${ $.provider}:data.default_shipping_address.entity_id + ${ $.provider}:data.default_shipping_address.entity_id + ${ $.provider}:data.default_shipping_address @@ -427,7 +435,7 @@ Add New Address - ${ $.provider}:data.customer_id + ${ $.provider}:data.customer_id @@ -437,8 +445,15 @@ - + + + + ns = customer_address_listing, index = customer_address_listing + ${ $.parentName } + + + ajax customer_address_edit 1 @@ -447,10 +462,16 @@ ${ $.parentName } ${ $.ns }.customer_address_form_data_source customer_address_form + + ${ $.externalProvider }:data.parent_id + + + ${ $.provider}:data.customer_id + - + false @@ -466,6 +487,8 @@ ${ $.provider }:data.customer.entity_id + ns = ${ $.ns }, index = actions:action + ns = ${ $.ns }, index = listing_massaction:massaction From e39ce7a3f2a7e6ee134cff9a097b7321756c32cf Mon Sep 17 00:00:00 2001 From: Joshua Bixler Date: Thu, 8 Nov 2018 11:00:03 -0600 Subject: [PATCH 115/704] Correct setFrom in Magento\Framework\Mail\Template to match Zend Message --- lib/internal/Magento/Framework/Mail/Message.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Mail/Message.php b/lib/internal/Magento/Framework/Mail/Message.php index 6f156e42dfdba..d3b8569a0b27e 100644 --- a/lib/internal/Magento/Framework/Mail/Message.php +++ b/lib/internal/Magento/Framework/Mail/Message.php @@ -90,9 +90,9 @@ public function getBody() /** * {@inheritdoc} */ - public function setFrom($fromAddress) + public function setFrom($fromAddress, $name = null) { - $this->zendMessage->setFrom($fromAddress); + $this->zendMessage->setFrom($fromAddress, $name); return $this; } From 99621b32f8c7d3d12f83db7e67f5f1c1ef3e7f31 Mon Sep 17 00:00:00 2001 From: Graham Wharton Date: Thu, 8 Nov 2018 17:12:58 +0000 Subject: [PATCH 116/704] Fixed setFrom function by deprecating old and introducing new function. --- lib/internal/Magento/Framework/Mail/Message.php | 15 ++++++++++++++- .../Framework/Mail/Template/TransportBuilder.php | 2 +- .../Mail/Template/TransportBuilderByStore.php | 2 +- .../Unit/Template/TransportBuilderByStoreTest.php | 4 ++-- .../Test/Unit/Template/TransportBuilderTest.php | 4 ++-- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/internal/Magento/Framework/Mail/Message.php b/lib/internal/Magento/Framework/Mail/Message.php index 6f156e42dfdba..bc7193f107bb5 100644 --- a/lib/internal/Magento/Framework/Mail/Message.php +++ b/lib/internal/Magento/Framework/Mail/Message.php @@ -89,10 +89,23 @@ public function getBody() /** * {@inheritdoc} + * + * @deprecated This function is missing the from name. The + * setFromAddress() function sets both from address and from name. + * @see setFromAddress() */ public function setFrom($fromAddress) { - $this->zendMessage->setFrom($fromAddress); + $this->setFromAddress($fromAddress, null); + return $this; + } + + /** + * {@inheritdoc} + */ + public function setFromAddress($fromAddress, $fromName = null) + { + $this->zendMessage->setFrom($fromAddress, $fromName); return $this; } diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php index 3a9117deb4b17..a7bb96122a84d 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php @@ -200,7 +200,7 @@ public function setFrom($from) public function setFromByStore($from, $store = null) { $result = $this->_senderResolver->resolve($from, $store); - $this->message->setFrom($result['email'], $result['name']); + $this->message->setFromAddress($result['email'], $result['name']); return $this; } diff --git a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php index baac867fd6ce8..85b1b181d4f9e 100644 --- a/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php +++ b/lib/internal/Magento/Framework/Mail/Template/TransportBuilderByStore.php @@ -54,7 +54,7 @@ public function __construct( public function setFromByStore($from, $store) { $result = $this->senderResolver->resolve($from, $store); - $this->message->setFrom($result['email'], $result['name']); + $this->message->setFromAddress($result['email'], $result['name']); return $this; } diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php index 80df2887a3a93..a28dbcd291baf 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderByStoreTest.php @@ -55,8 +55,8 @@ public function testSetFromByStore() ->with($sender, $store) ->willReturn($sender); $this->messageMock->expects($this->once()) - ->method('setFrom') - ->with('from@example.com', 'name') + ->method('setFromAddress') + ->with($sender['email'], $sender['name']) ->willReturnSelf(); $this->model->setFromByStore($sender, $store); diff --git a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php index 5f20a790a2d32..b476eecd7f59f 100644 --- a/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php +++ b/lib/internal/Magento/Framework/Mail/Test/Unit/Template/TransportBuilderTest.php @@ -176,8 +176,8 @@ public function testSetFromByStore() ->with($sender, $store) ->willReturn($sender); $this->messageMock->expects($this->once()) - ->method('setFrom') - ->with('from@example.com', 'name') + ->method('setFromAddress') + ->with($sender['email'], $sender['name']) ->willReturnSelf(); $this->builder->setFromByStore($sender, $store); From 1becee9bb4114318c89f54b45c10b6b60cdc9cee Mon Sep 17 00:00:00 2001 From: Alex Ghiban Date: Thu, 8 Nov 2018 16:38:02 -0500 Subject: [PATCH 117/704] bug-fix #16074 - Fix setting the default theme in the middle of the product page, regardless of the design exception. --- .../Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php index 216bc16968fcb..663fbbacf960d 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php @@ -136,7 +136,6 @@ public function collect(ProductInterface $product, ProductRenderInterface $produ public function emulateImageCreating(ProductInterface $product, $imageCode, $storeId, ImageInterface $image) { $this->storeManager->setCurrentStore($storeId); - $this->design->setDefaultDesignTheme(); $imageHelper = $this->imageFactory->create(); $imageHelper->init($product, $imageCode); From b8940db9514c672c9e191bbb33172884a524a993 Mon Sep 17 00:00:00 2001 From: Alex Ghiban Date: Fri, 9 Nov 2018 02:46:30 -0500 Subject: [PATCH 118/704] Revert "bug-fix #16074 - Fix setting the default theme in the middle of the" This reverts commit 1becee9bb4114318c89f54b45c10b6b60cdc9cee. --- .../Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php index 663fbbacf960d..216bc16968fcb 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php @@ -136,6 +136,7 @@ public function collect(ProductInterface $product, ProductRenderInterface $produ public function emulateImageCreating(ProductInterface $product, $imageCode, $storeId, ImageInterface $image) { $this->storeManager->setCurrentStore($storeId); + $this->design->setDefaultDesignTheme(); $imageHelper = $this->imageFactory->create(); $imageHelper->init($product, $imageCode); From fd26b94dc1950845a135879937ce7ce0d8f8b8b4 Mon Sep 17 00:00:00 2001 From: Alex Ghiban Date: Fri, 9 Nov 2018 02:50:37 -0500 Subject: [PATCH 119/704] bug-fix #16074 Restore current theme once the images creation is emulated. --- .../Ui/DataProvider/Product/Listing/Collector/Image.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php index 216bc16968fcb..ae2b96e6e838b 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php @@ -14,6 +14,7 @@ use Magento\Catalog\Model\Product\Image\NotLoadInfoImageException; use Magento\Catalog\Ui\DataProvider\Product\ProductRenderCollectorInterface; use Magento\Framework\App\State; +use Magento\Framework\View\Design\ThemeInterface; use Magento\Framework\View\DesignInterface; use Magento\Store\Model\StoreManager; use Magento\Store\Model\StoreManagerInterface; @@ -92,6 +93,8 @@ public function __construct( public function collect(ProductInterface $product, ProductRenderInterface $productRender) { $images = []; + /** @var ThemeInterface $currentTheme */ + $currentTheme = $this->design->getDesignTheme(); foreach ($this->imageCodes as $imageCode) { /** @var ImageInterface $image */ @@ -120,6 +123,7 @@ public function collect(ProductInterface $product, ProductRenderInterface $produ $images[] = $image; } + $this->design->setDesignTheme($currentTheme); $productRender->setImages($images); } From 97da50434f0577ae602dfd79493323793f91baee Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Fri, 9 Nov 2018 10:42:59 +0200 Subject: [PATCH 120/704] MAGETWO-95249: [Part 1] Implement handling of large number of addresses on admin edit customer page - Add custom DataProvider for customer addresses listing; --- .../Listing/Address/DataProvider.php | 52 +++++++++++++++++++ .../ui_component/customer_address_listing.xml | 2 +- 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php new file mode 100644 index 0000000000000..df92e7c1c9421 --- /dev/null +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php @@ -0,0 +1,52 @@ +collection = $collectionFactory->create(); + } + + /** + * Add country key for default billing/shipping blocks on customer addresses tab + * + * @return array + */ + public function getData(): array + { + $collection = $this->getCollection(); + $data = $collection->toArray(); + foreach ($data['items'] as $key => $item) { + if (isset($item['country_id']) && !isset($item['country'])) { + $data['items'][$key]['country'] = $item['country_id']; + } + } + + return $data; + } +} diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml index 6c97a6da5e9e2..7fb8c49749f2d 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml @@ -28,7 +28,7 @@ Magento_Customer::manage - + id entity_id From fea9aafd36096a92304c8f0fc3d223637d6d665d Mon Sep 17 00:00:00 2001 From: eduard13 Date: Fri, 9 Nov 2018 15:04:09 +0200 Subject: [PATCH 121/704] Adjusting the Unit Test for the isCurrent method --- .../Unit/Element/Html/Link/CurrentTest.php | 63 ++++++++++++------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php index 909748722a081..9c720f0f48dc3 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php @@ -17,11 +17,6 @@ class CurrentTest extends \PHPUnit\Framework\TestCase */ protected $_requestMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $_defaultPathMock; - /** * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ @@ -32,7 +27,6 @@ protected function setUp() $this->_objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->_urlBuilderMock = $this->createMock(\Magento\Framework\UrlInterface::class); $this->_requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); - $this->_defaultPathMock = $this->createMock(\Magento\Framework\App\DefaultPathInterface::class); } public function testGetUrl() @@ -60,31 +54,54 @@ public function testIsCurrentIfIsset() $this->assertTrue($link->isCurrent()); } - public function testIsCurrent() + /** + * Test if the current url is the same as link path + * + * @dataProvider linkPathProvider + * @param string $linkPath + * @param string $currentPathInfo + * @param bool $expected + * @return void + */ + public function testIsCurrent($linkPath, $currentPathInfo, $expected) { - $path = 'test/path'; - $url = 'http://example.com/a/b'; - - $this->_requestMock->expects($this->once())->method('getModuleName')->will($this->returnValue('a')); - $this->_requestMock->expects($this->once())->method('getControllerName')->will($this->returnValue('b')); - $this->_requestMock->expects($this->once())->method('getActionName')->will($this->returnValue('d')); - $this->_defaultPathMock->expects($this->atLeastOnce())->method('getPart')->will($this->returnValue('d')); - - $this->_urlBuilderMock->expects($this->at(0))->method('getUrl')->with($path)->will($this->returnValue($url)); - $this->_urlBuilderMock->expects($this->at(1))->method('getUrl')->with('a/b')->will($this->returnValue($url)); - - $this->_requestMock->expects($this->once())->method('getControllerName')->will($this->returnValue('b')); + $baseUrl = 'http://example.com/'; + $trimmed = trim($currentPathInfo, '/'); + + $this->_requestMock->expects($this->any())->method('getPathInfo')->willReturn($currentPathInfo); + $this->_urlBuilderMock->expects($this->at(0)) + ->method('getUrl') + ->with($linkPath) + ->will($this->returnValue($baseUrl . $linkPath)); + $this->_urlBuilderMock->expects($this->at(1)) + ->method('getUrl') + ->with($trimmed) + ->will($this->returnValue($baseUrl . $trimmed)); /** @var \Magento\Framework\View\Element\Html\Link\Current $link */ $link = $this->_objectManager->getObject( \Magento\Framework\View\Element\Html\Link\Current::class, [ 'urlBuilder' => $this->_urlBuilderMock, - 'request' => $this->_requestMock, - 'defaultPath' => $this->_defaultPathMock + 'request' => $this->_requestMock ] ); - $link->setPath($path); - $this->assertTrue($link->isCurrent()); + + $link->setCurrent(false); + $link->setPath($linkPath); + $this->assertEquals($expected, $link->isCurrent()); + } + + /** + * @return array + */ + public function linkPathProvider() + { + return [ + ['test/index', '/test/index/', true], + ['test/index/index', '/test/index/index/', true], + ['test/route', '/test/index/', false], + ['test/index', '/test/', false] + ]; } public function testIsCurrentFalse() From 0e8cd9ffc0b0ce1da6d3731fb4b21b68b40b5747 Mon Sep 17 00:00:00 2001 From: NazarKlovanych Date: Fri, 9 Nov 2018 18:07:18 +0200 Subject: [PATCH 122/704] Calling getCurrentUrl on Store will wrongly add "___store" --- app/code/Magento/Store/Model/Store.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index af25957257421..04e98ed88db23 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -1164,11 +1164,12 @@ public function isDefault() * Retrieve current url for store * * @param bool $fromStore + * @param bool $clearUrl * @return string * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ - public function getCurrentUrl($fromStore = true) + public function getCurrentUrl($fromStore = true, $clearUrl = false) { $sidQueryParam = $this->_sidResolver->getSessionIdQueryParam($this->_getSession()); $requestString = $this->_url->escape(ltrim($this->_request->getRequestString(), '/')); @@ -1217,6 +1218,10 @@ public function getCurrentUrl($fromStore = true) $currentUrlQueryParams = array_merge($requestString, $storeParsedQuery); + if ($clearUrl !== false) { + $currentUrlQueryParams = false; + } + $currentUrl = $storeParsedUrl['scheme'] . '://' . $storeParsedUrl['host'] From 300772deea6e9f8678dd964807dbb31d327b1ad1 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych Date: Sun, 11 Nov 2018 22:34:04 +0200 Subject: [PATCH 123/704] Fix issue 18941 --- app/code/Magento/Store/Model/Store.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php index 04e98ed88db23..4282efb6e55e8 100644 --- a/app/code/Magento/Store/Model/Store.php +++ b/app/code/Magento/Store/Model/Store.php @@ -1164,18 +1164,21 @@ public function isDefault() * Retrieve current url for store * * @param bool $fromStore - * @param bool $clearUrl * @return string * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ - public function getCurrentUrl($fromStore = true, $clearUrl = false) + public function getCurrentUrl($fromStore = true) { $sidQueryParam = $this->_sidResolver->getSessionIdQueryParam($this->_getSession()); $requestString = $this->_url->escape(ltrim($this->_request->getRequestString(), '/')); $storeUrl = $this->getUrl('', ['_secure' => $this->_storeManager->getStore()->isCurrentlySecure()]); + if ($this->_config->getValue(self::XML_PATH_STORE_IN_URL)) { + $storeUrl = preg_replace('/\/'.$this->getCode().'{1}/','',$storeUrl); + } + if (!filter_var($storeUrl, FILTER_VALIDATE_URL)) { return $storeUrl; } @@ -1218,10 +1221,6 @@ public function getCurrentUrl($fromStore = true, $clearUrl = false) $currentUrlQueryParams = array_merge($requestString, $storeParsedQuery); - if ($clearUrl !== false) { - $currentUrlQueryParams = false; - } - $currentUrl = $storeParsedUrl['scheme'] . '://' . $storeParsedUrl['host'] From 769e46d47a237f1d06e53c2c52341d45cf610531 Mon Sep 17 00:00:00 2001 From: eduard13 Date: Mon, 12 Nov 2018 13:52:54 +0200 Subject: [PATCH 124/704] Improving the current link logic, by covering all the possible cases for default parts --- .../View/Element/Html/Link/Current.php | 21 +++------ .../Unit/Element/Html/Link/CurrentTest.php | 47 ++++++++----------- 2 files changed, 25 insertions(+), 43 deletions(-) diff --git a/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php b/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php index cb29c67a2ca30..d5da6cefae5b9 100644 --- a/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php +++ b/lib/internal/Magento/Framework/View/Element/Html/Link/Current.php @@ -64,14 +64,15 @@ public function getHref() private function getMca() { $routeParts = [ - 'module' => $this->_request->getModuleName(), - 'controller' => $this->_request->getControllerName(), - 'action' => $this->_request->getActionName(), + $this->_request->getModuleName(), + $this->_request->getControllerName(), + $this->_request->getActionName(), ]; $parts = []; + $pathParts = explode('/', $this->getPath()); foreach ($routeParts as $key => $value) { - if (!empty($value) && $value != $this->_defaultPath->getPart($key)) { + if (isset($pathParts[$key]) && $pathParts[$key] === $value) { $parts[] = $value; } } @@ -85,7 +86,7 @@ private function getMca() */ public function isCurrent() { - return $this->getCurrent() || $this->getUrl($this->getPath()) == $this->getUrl($this->getPathInfo()); + return $this->getCurrent() || $this->getUrl($this->getPath()) == $this->getUrl($this->getMca()); } /** @@ -151,14 +152,4 @@ private function getAttributesHtml() return $attributesHtml; } - - /** - * Get current page path info - * - * @return string - */ - private function getPathInfo() - { - return trim($this->_request->getPathInfo(), '/'); - } } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php index 9c720f0f48dc3..e11468061c9ec 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/Html/Link/CurrentTest.php @@ -57,26 +57,31 @@ public function testIsCurrentIfIsset() /** * Test if the current url is the same as link path * - * @dataProvider linkPathProvider - * @param string $linkPath - * @param string $currentPathInfo - * @param bool $expected * @return void */ - public function testIsCurrent($linkPath, $currentPathInfo, $expected) + public function testIsCurrent() { - $baseUrl = 'http://example.com/'; - $trimmed = trim($currentPathInfo, '/'); + $path = 'test/index'; + $url = 'http://example.com/test/index'; - $this->_requestMock->expects($this->any())->method('getPathInfo')->willReturn($currentPathInfo); + $this->_requestMock->expects($this->once()) + ->method('getModuleName') + ->will($this->returnValue('test')); + $this->_requestMock->expects($this->once()) + ->method('getControllerName') + ->will($this->returnValue('index')); + $this->_requestMock->expects($this->once()) + ->method('getActionName') + ->will($this->returnValue('index')); $this->_urlBuilderMock->expects($this->at(0)) ->method('getUrl') - ->with($linkPath) - ->will($this->returnValue($baseUrl . $linkPath)); + ->with($path) + ->will($this->returnValue($url)); $this->_urlBuilderMock->expects($this->at(1)) ->method('getUrl') - ->with($trimmed) - ->will($this->returnValue($baseUrl . $trimmed)); + ->with('test/index') + ->will($this->returnValue($url)); + /** @var \Magento\Framework\View\Element\Html\Link\Current $link */ $link = $this->_objectManager->getObject( \Magento\Framework\View\Element\Html\Link\Current::class, @@ -86,22 +91,8 @@ public function testIsCurrent($linkPath, $currentPathInfo, $expected) ] ); - $link->setCurrent(false); - $link->setPath($linkPath); - $this->assertEquals($expected, $link->isCurrent()); - } - - /** - * @return array - */ - public function linkPathProvider() - { - return [ - ['test/index', '/test/index/', true], - ['test/index/index', '/test/index/index/', true], - ['test/route', '/test/index/', false], - ['test/index', '/test/', false] - ]; + $link->setPath($path); + $this->assertTrue($link->isCurrent()); } public function testIsCurrentFalse() From 479a95f1e3fb58044d23f15e904f221b2e017574 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov Date: Mon, 12 Nov 2018 15:01:31 +0200 Subject: [PATCH 125/704] MAGETWO-96208: Mass action "Delete" removes all addresses --- .../Controller/Adminhtml/Address/MassDelete.php | 8 +++++++- .../ResourceModel/Address/Grid/Collection.php | 16 ---------------- .../Component/Listing/Address/DataProvider.php | 14 +++++++++++++- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php index 09523f9e6a956..10638739f2b0a 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/MassDelete.php @@ -87,12 +87,18 @@ public function __construct( */ public function execute(): Json { + $customerData = $this->_session->getData('customer_data'); /** @var \Magento\Customer\Model\ResourceModel\Address\Collection $collection */ $collection = $this->filter->getCollection($this->collectionFactory->create()); - $collectionSize = $collection->getSize(); $error = false; try { + if ($customerData && $customerData['customer_id']) { + $collection->addFieldToFilter('parent_id', $customerData['customer_id']); + } else { + throw new \Exception(); + } + $collectionSize = $collection->getSize(); /** @var \Magento\Customer\Model\Address $address */ foreach ($collection as $address) { $this->addressRepository->deleteById($address->getId()); diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php index 09aef78b6cebc..b0e4c3d8dcc24 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Address/Grid/Collection.php @@ -76,22 +76,6 @@ public function __construct( ); } - /** - * Resource initialization - * - * @return $this - */ - protected function _initSelect() - { - parent::_initSelect(); - $parentId = $this->context->getRequestParam('parent_id'); - if ($parentId !== null) { - $this->getSelect()->where('parent_id=?', $parentId); - } - - return $this; - } - /** * @inheritdoc * diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php index df92e7c1c9421..e034e5a894e96 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php @@ -12,11 +12,17 @@ */ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider { + /** + * @var \Magento\Framework\App\RequestInterface $request, + */ + private $request; + /** * @param string $name * @param string $primaryFieldName * @param string $requestFieldName * @param CollectionFactory $collectionFactory + * @param \Magento\Framework\App\RequestInterface $request * @param array $meta * @param array $data */ @@ -25,11 +31,13 @@ public function __construct( $primaryFieldName, $requestFieldName, CollectionFactory $collectionFactory, + \Magento\Framework\App\RequestInterface $request, array $meta = [], array $data = [] ) { parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); $this->collection = $collectionFactory->create(); + $this->request = $request; } /** @@ -40,7 +48,11 @@ public function __construct( public function getData(): array { $collection = $this->getCollection(); - $data = $collection->toArray(); + $data['items'] = []; + if ($this->request->getParam('parent_id')) { + $collection->addFieldToFilter('parent_id', $this->request->getParam('parent_id')); + $data = $collection->toArray(); + } foreach ($data['items'] as $key => $item) { if (isset($item['country_id']) && !isset($item['country'])) { $data['items'][$key]['country'] = $item['country_id']; From 36bc920210d45be31dbd724e3cff1675feae8b63 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Mon, 12 Nov 2018 19:32:01 +0200 Subject: [PATCH 126/704] MAGETWO-96182: Adopt integration and API tests for implemented changes --- .../ui_component/customer_address_listing.xml | 26 ++++---- .../Controller/Adminhtml/Address/SaveTest.php | 60 +------------------ 2 files changed, 13 insertions(+), 73 deletions(-) diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml index 4db5a7fb8d545..b138a81e395a3 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml @@ -63,22 +63,20 @@ - - - - - true - - Are you sure to delete selected address? - Delete items - - - delete - Delete + + + + true + + Are you sure to delete selected address? + Delete items + + delete + Delete - - + + diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php index 5a4a426b58cda..cbfa794f94dc0 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Address/SaveTest.php @@ -89,19 +89,9 @@ public function testSaveActionWithValidAddressData() $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); - /** - * Check that customer data were cleaned after it was saved successfully - */ + /** Check that customer data were cleaned after it was saved successfully*/ $this->assertEmpty($this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerData()); - /** - * Check that success message is set - */ - $this->assertSessionMessages( - $this->logicalNot($this->isEmpty()), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - $customer = $this->customerRepository->getById($customerId); $this->assertEquals('Firstname', $customer->getFirstname()); @@ -148,14 +138,6 @@ public function testSaveActionWithDefaultShippingAndBilling() */ $this->assertEmpty($this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerData()); - /** - * Check that success message is set - */ - $this->assertSessionMessages( - $this->logicalNot($this->isEmpty()), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - /** * Remove stored customer from registry */ @@ -204,50 +186,10 @@ public function testSaveActionWithExistingAdresses() */ $this->assertEmpty($this->objectManager->get(\Magento\Backend\Model\Session::class)->getCustomerData()); - /** - * Check that success message is set - */ - $this->assertSessionMessages( - $this->logicalNot($this->isEmpty()), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - $customer = $this->customerRepository->getById($customerId); $this->assertEquals('test firstname', $customer->getFirstname()); $addresses = $customer->getAddresses(); $this->assertCount(4, $addresses); } - - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - * @magentoDataFixture Magento/Customer/_files/customer_address.php - */ - public function testValidateCustomerWithAddressFailure() - { - $customer = $this->customerRepository->get('customer@example.com'); - $customerId = $customer->getId(); - $post = [ - 'parent_id' => $customerId, - 'firstname' => '', - 'lastname' => '', - 'street' => ['update street'], - 'city' => 'update city', - 'postcode' => '01001', - 'telephone' => '', - ]; - $this->getRequest()->setPostValue($post)->setMethod(HttpRequest::METHOD_POST); - - $this->objectManager->get(\Magento\Backend\Model\Session::class)->setCustomerFormData($post); - - $this->customerAddress->execute(); - - /** - * Check that errors was generated and set to session - */ - $this->assertSessionMessages( - $this->equalTo(['One or more input exceptions have occurred.']), - \Magento\Framework\Message\MessageInterface::TYPE_ERROR - ); - } } From 329f4194904dc0da6be8958278f5c9ebad2b54d5 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko Date: Mon, 12 Nov 2018 13:23:06 -0600 Subject: [PATCH 127/704] MAGETWO-96178: Customer addresses should be saved using Ajax --- .../Listing/Address/DataProvider.php | 10 +++++- .../ui_component/customer_address_form.xml | 4 +-- .../web/js/form/components/insert-listing.js | 36 +++++++++---------- .../adminhtml/web/js/form/element/country.js | 27 ++++++++++++++ .../adminhtml/web/js/form/element/region.js | 27 ++++++++++++++ .../view/adminhtml/web/js/grid/massactions.js | 3 +- .../web/template/default-address.html | 4 +-- 7 files changed, 87 insertions(+), 24 deletions(-) create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/form/element/country.js create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/form/element/region.js diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php index e034e5a894e96..75f9dca9fd6cf 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/DataProvider.php @@ -17,12 +17,18 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider */ private $request; + /** + * @var \Magento\Directory\Model\CountryFactory + */ + private $countryDirectory; + /** * @param string $name * @param string $primaryFieldName * @param string $requestFieldName * @param CollectionFactory $collectionFactory * @param \Magento\Framework\App\RequestInterface $request + * @param \Magento\Directory\Model\CountryFactory $countryFactory, * @param array $meta * @param array $data */ @@ -32,11 +38,13 @@ public function __construct( $requestFieldName, CollectionFactory $collectionFactory, \Magento\Framework\App\RequestInterface $request, + \Magento\Directory\Model\CountryFactory $countryFactory, array $meta = [], array $data = [] ) { parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); $this->collection = $collectionFactory->create(); + $this->countryDirectory = $countryFactory->create(); $this->request = $request; } @@ -55,7 +63,7 @@ public function getData(): array } foreach ($data['items'] as $key => $item) { if (isset($item['country_id']) && !isset($item['country'])) { - $data['items'][$key]['country'] = $item['country_id']; + $data['items'][$key]['country'] = $this->countryDirectory->loadByCode($item['country_id'])->getName(); } } diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index bcc5b86c5e65d..4aeb7d67ace68 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -181,7 +181,7 @@
- + true @@ -196,7 +196,7 @@ - + true diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-listing.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-listing.js index 0dd828b03b419..4e8952e9e2a9d 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-listing.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-listing.js @@ -4,8 +4,9 @@ */ define([ - 'Magento_Ui/js/form/components/insert-listing' -], function (Insert) { + 'Magento_Ui/js/form/components/insert-listing', + 'underscore' +], function (Insert, _) { 'use strict'; return Insert.extend({ @@ -25,29 +26,28 @@ define([ }, deleteAction: function (data) { + this._delete([parseFloat(data[data['id_field_name']])]); + }, + + deleteMassaction: function (data) { + var ids = _.map(data, function (val) { + return parseFloat(val); + }); + + this._delete(ids); + }, + + _delete: function (ids) { var defaultShippingId = parseFloat(this.source.get('data.default_shipping_address.entity_id')), defaultBillingId = parseFloat(this.source.get('data.default_billing_address.entity_id')); - if (parseFloat(data[data['id_field_name']]) === defaultShippingId) { + if (ids.indexOf(defaultShippingId) !== -1) { this.source.set('data.default_shipping_address', []); } - if (parseFloat(data[data['id_field_name']]) === defaultBillingId) { + + if (ids.indexOf(defaultBillingId) !== -1) { this.source.set('data.default_billing_address', []); } - }, - - //TODO: release logic with massaction - deleteMassaction: function (data) { - debugger; - // var defaultShippingId = parseFloat(this.source.get('data.default_shipping_address.entity_id')), - // defaultBillingId = parseFloat(this.source.get('data.default_billing_address.entity_id')); - // - // if (parseFloat(data[data['id_field_name']]) === defaultShippingId) { - // this.source.set('data.default_shipping_address', []); - // } - // if (parseFloat(data[data['id_field_name']]) === defaultBillingId) { - // this.source.set('data.default_billing_address', []); - // } } }); }); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/element/country.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/element/country.js new file mode 100644 index 0000000000000..bc60cf7a08fda --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/element/country.js @@ -0,0 +1,27 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * @api + */ +define([ + 'Magento_Ui/js/form/element/country' +], function (Country) { + 'use strict'; + + return Country.extend({ + defaults: { + countryScope: 'data.country' + }, + + setDifferedFromDefault: function (value) { + this._super(); + + if (value) { + this.source.set(this.countryScope, this.indexedOptions[value].label); + } + } + }); +}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/element/region.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/element/region.js new file mode 100644 index 0000000000000..a9013577a1026 --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/element/region.js @@ -0,0 +1,27 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * @api + */ +define([ + 'Magento_Ui/js/form/element/region' +], function (Region) { + 'use strict'; + + return Region.extend({ + defaults: { + regionScope: 'data.region' + }, + + setDifferedFromDefault: function (value) { + this._super(); + + if (parseFloat(value)) { + this.source.set(this.regionScope, this.indexedOptions[value].label); + } + } + }); +}); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js b/app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js index 1aefa2e249958..2477ef2672e68 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/grid/massactions.js @@ -49,11 +49,12 @@ define([ _.extend(selections, data.params || {}); + console.log(selections); this.request(action.url, selections).done(function (response) { if (!response.error) { this.trigger('massaction', { action: action.type, - data: selections + data: this.selections().selected() }); } }.bind(this)); diff --git a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html index 96158e9921e22..96de88a1145f4 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html +++ b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html @@ -16,8 +16,8 @@
- - + +
From ccda9172c70ddf00435641624d3814061e23f791 Mon Sep 17 00:00:00 2001 From: Nathan Smith Date: Mon, 12 Nov 2018 16:55:54 -0600 Subject: [PATCH 128/704] MAGETWO-93181: Grouped product doesn't take care about his Linked Products when SalableQuantity < ProductLink.ExtensionAttributes.Qty after Source Deduction - Added condition --- .../Catalog/Controller/Adminhtml/Category/Index.php | 1 + .../GroupedProduct/Model/Product/Type/Grouped.php | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php index 5089b37f90c58..47969a4eca9ec 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php @@ -7,6 +7,7 @@ namespace Magento\Catalog\Controller\Adminhtml\Category; use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface; +use Magento\Framework\Url\EncoderInterface; class Index extends \Magento\Catalog\Controller\Adminhtml\Category implements HttpGetActionInterface { diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php index afa94e2b0b7a2..e54cb79b7b4e7 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php @@ -208,9 +208,10 @@ public function getParentIdsByChild($childId) * Retrieve array of associated products * * @param \Magento\Catalog\Model\Product $product + * @param bool $includeOutOfStock * @return array */ - public function getAssociatedProducts($product) + public function getAssociatedProducts($product, bool $includeOutOfStock = true) { if (!$product->hasData($this->_keyAssociatedProducts)) { $associatedProducts = []; @@ -228,7 +229,9 @@ public function getAssociatedProducts($product) ['in' => $this->getStatusFilters($product)] ); - $this->stockHelper->addIsInStockFilterToCollection($collection); + if (!$includeOutOfStock) { + $this->stockHelper->addIsInStockFilterToCollection($collection); + } foreach ($collection as $item) { $associatedProducts[] = $item; @@ -349,7 +352,7 @@ public function getAssociatedProductCollection($product) protected function getProductInfo(\Magento\Framework\DataObject $buyRequest, $product, $isStrictProcessMode) { $productsInfo = $buyRequest->getSuperGroup() ?: []; - $associatedProducts = $this->getAssociatedProducts($product); + $associatedProducts = $this->getAssociatedProducts($product, !empty($productsInfo)); if (!is_array($productsInfo)) { return __('Please specify the quantity of product(s).')->render(); From 47e42a969d443715bf483f9e90eabfd2a141c930 Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi Date: Mon, 12 Nov 2018 17:57:46 -0600 Subject: [PATCH 129/704] ENGCOM-3448: bug-fix #16074 - Fix theme change in middle of product page. #19124 - Fixed docblock --- .../Ui/DataProvider/Product/Listing/Collector/Image.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php index ae2b96e6e838b..ef86e1727c447 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php @@ -23,7 +23,6 @@ * Collect enough information about image rendering on front * If you want to add new image, that should render on front you need * to configure this class in di.xml - * */ class Image implements ProductRenderCollectorInterface { @@ -128,6 +127,8 @@ public function collect(ProductInterface $product, ProductRenderInterface $produ } /** + * Emulate image creation + * * Callback in which we emulate initialize default design theme, depends on current store, be settings store id * from render info * From ffa44b557173be5fec38dac0494c42925e2ae677 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy Date: Tue, 13 Nov 2018 10:50:09 +0200 Subject: [PATCH 130/704] MAGETWO-96182: Adopt integration and API tests for implemented changes - Change mass delete action in customer address listing structure; --- .../ui_component/customer_address_listing.xml | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml index b138a81e395a3..6d59977cda140 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_listing.xml @@ -63,20 +63,18 @@
- - - - true - - Are you sure to delete selected address? - Delete items - - - delete - Delete - - - + + + true + + Are you sure to delete selected address? + Delete items + + + delete + + + From 6afe3938708a4c166dfab30e60f7793e1b236963 Mon Sep 17 00:00:00 2001 From: Nathan Smith Date: Tue, 13 Nov 2018 09:36:53 -0600 Subject: [PATCH 131/704] MAGETWO-93181: Grouped product doesn't take care about his Linked Products when SalableQuantity < ProductLink.ExtensionAttributes.Qty after Source Deduction - Added test for admin grid --- .../Test/AdminGroupedProductsListTest.xml | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml new file mode 100644 index 0000000000000..b59cf1e2175d8 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/AdminGroupedProductsListTest.xml @@ -0,0 +1,73 @@ + + + + + + + + + + <description value="Products in group should show in admin list even when they are out of stock"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-93181"/> + <group value="GroupedProduct"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="category1"/> + <createData entity="SimpleProduct" stepKey="simpleProduct1"> + <requiredEntity createDataKey="category1"/> + </createData> + <!--Out of Stock--> + <createData entity="SimpleProduct4" stepKey="simpleProduct2"> + <requiredEntity createDataKey="category1"/> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="category1" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Create product --> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="adminProductIndexPageAdd"/> + <waitForPageLoad stepKey="waitForProductIndexPageLoad"/> + <actionGroup ref="goToCreateProductPage" stepKey="goToCreateProductPage"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + <actionGroup ref="fillGroupedProductForm" stepKey="fillMainProductForm"> + <argument name="product" value="GroupedProduct"/> + </actionGroup> + <!-- Add two simple products to grouped product --> + <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollToSection"/> + <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductSection"/> + <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup"/> + <waitForElementVisible selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="waitForFilter"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku1"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="checkOption1"/> + <actionGroup ref="filterProductGridBySku" stepKey="filterProductGridBySku2"> + <argument name="product" value="$$simpleProduct2$$"/> + </actionGroup> + <checkOption selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="checkOption2"/> + <click selector="{{AdminAddProductsToGroupPanel.addSelectedProducts}}" stepKey="addSelectedProducts"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + + <!-- Save product --> + <actionGroup ref="saveProductForm" stepKey="saveProductForm"/> + <waitForPageLoad stepKey="waitForPageLoad2"/> + + <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollToProducts"/> + + <waitForText userInput="$$simpleProduct1.name$$" stepKey="assertProductIsInTheList"/> + <waitForText userInput="$$simpleProduct2.name$$" stepKey="assertProduct2IsInTheList"/> + </test> +</tests> From 1cb7ad9ec01b3735485e8edbe298a535c600f1ca Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Tue, 13 Nov 2018 09:39:57 -0600 Subject: [PATCH 132/704] MAGETWO-93181: Grouped product doesn't take care about his Linked Products when SalableQuantity < ProductLink.ExtensionAttributes.Qty after Source Deduction - Static fixed --- .../Magento/Catalog/Controller/Adminhtml/Category/Index.php | 4 +++- .../Magento/GroupedProduct/Model/Product/Type/Grouped.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php index 47969a4eca9ec..a5be6223bee75 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Index.php @@ -7,8 +7,10 @@ namespace Magento\Catalog\Controller\Adminhtml\Category; use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface; -use Magento\Framework\Url\EncoderInterface; +/** + * Controller for category listing + */ class Index extends \Magento\Catalog\Controller\Adminhtml\Category implements HttpGetActionInterface { /** diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php index e54cb79b7b4e7..2f8249397819a 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php @@ -230,7 +230,7 @@ public function getAssociatedProducts($product, bool $includeOutOfStock = true) ); if (!$includeOutOfStock) { - $this->stockHelper->addIsInStockFilterToCollection($collection); + $this->stockHelper->addIsInStockFilterToCollection($collection); } foreach ($collection as $item) { From a0f675dd403348a52ee7f64176a75df74325f627 Mon Sep 17 00:00:00 2001 From: Yuliya Labudova <Yuliya_Labudova@epam.com> Date: Tue, 13 Nov 2018 18:50:07 +0300 Subject: [PATCH 133/704] MAGETWO-95809: Item row total display incorrect value in API response - Fix static test --- app/code/Magento/Sales/Plugin/DataObjectProcessor.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Sales/Plugin/DataObjectProcessor.php b/app/code/Magento/Sales/Plugin/DataObjectProcessor.php index 5c61cdda9efc2..2ebf0364c86a3 100644 --- a/app/code/Magento/Sales/Plugin/DataObjectProcessor.php +++ b/app/code/Magento/Sales/Plugin/DataObjectProcessor.php @@ -10,7 +10,7 @@ use Magento\Framework\Reflection\DataObjectProcessor as Subject; use Magento\Sales\Api\Data\OrderItemInterface; use Magento\Sales\Model\Order\Item as OrderItem; -use Magento\Weee\Block\Item\Price\Renderer; +use Magento\Sales\Block\Adminhtml\Items\Column\DefaultColumn; /** * Class for changing row total in response. @@ -18,15 +18,15 @@ class DataObjectProcessor { /** - * @var Renderer + * @var DefaultColumn */ private $priceRenderer; /** - * @param Renderer $priceRenderer + * @param DefaultColumn $priceRenderer */ public function __construct( - Renderer $priceRenderer + DefaultColumn $priceRenderer ) { $this->priceRenderer = $priceRenderer; } From 4b6100d1e991fc56f7ff9f8e0f0be0db7192328e Mon Sep 17 00:00:00 2001 From: Sviatoslav Mankivskyi <mankivsk@adobe.com> Date: Tue, 13 Nov 2018 10:24:56 -0600 Subject: [PATCH 134/704] ENGCOM-3448: bug-fix #16074 - Fix theme change in middle of product page. #19124 - Fixed docblock --- .../Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php index ef86e1727c447..b0ce31eedf8b0 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Listing/Collector/Image.php @@ -128,7 +128,7 @@ public function collect(ProductInterface $product, ProductRenderInterface $produ /** * Emulate image creation - * + * * Callback in which we emulate initialize default design theme, depends on current store, be settings store id * from render info * From 08ac92e88a9420e358818f29091723454d937f9c Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Tue, 13 Nov 2018 19:02:27 +0200 Subject: [PATCH 135/704] MAGETWO-96306: Customer address street data is displayed incorrectly on default billing/shipping blocks --- .../Customer/view/adminhtml/web/template/default-address.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html index 96de88a1145f4..1dd583b990202 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html +++ b/app/code/Magento/Customer/view/adminhtml/web/template/default-address.html @@ -17,7 +17,8 @@ </if> <if args="address.street"> <text args="address.street" if="_.isString(address.street)"/> - <text args="_.values(address.street).join(', ')" ifnot="_.isString(address.street)"/> + <text args="_.values(address.street).filter(value => _.isString(value)).join(', ')" + ifnot="_.isString(address.street)"/> <br/> </if> <text args="address.city + ', '" if="address.city"/> From ce1a7cec2053f19753c5b003fae74d538428a7ee Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Tue, 13 Nov 2018 19:31:29 +0200 Subject: [PATCH 136/704] MAGETWO-96209: Customer address grid actions send GET instead of POST request --- .../Magento/Customer/Controller/Adminhtml/Address/Delete.php | 4 ++-- .../Customer/Ui/Component/Listing/Address/Column/Actions.php | 1 + .../Customer/view/adminhtml/web/js/grid/columns/actions.js | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php index 65da3bca49e9b..711cd2473cdc5 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php @@ -8,7 +8,7 @@ namespace Magento\Customer\Controller\Adminhtml\Address; use Magento\Backend\App\Action; -use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\Result\JsonFactory; use Magento\Customer\Api\AddressRepositoryInterface; @@ -17,7 +17,7 @@ /** * Button for deletion of customer address in admin */ -class Delete extends Action implements HttpGetActionInterface +class Delete extends Action implements HttpPostActionInterface { /** * Authorization level of a basic admin session diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php index 490a14169e7b7..7da6d3a6a86c5 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php @@ -113,6 +113,7 @@ public function prepareDataSource(array $dataSource): array ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] ), 'label' => __('Delete'), + 'post' => true, 'isAjax' => true, 'confirm' => [ 'title' => __('Delete address'), diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js b/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js index 43ab06c8014ee..4a62ce2e6eccf 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js @@ -18,8 +18,9 @@ define([ return Actions.extend({ defaults: { ajaxSettings: { - method: 'GET', - dataType: 'json' + method: 'POST', + dataType: 'json', + formKey: $('input[name="form_key"]').val() }, listens: { action: 'onAction' From f6a6d11bc0219bc4847745d264f8991d04d5372e Mon Sep 17 00:00:00 2001 From: Graham Wharton <graham@gwharton.me.uk> Date: Tue, 13 Nov 2018 20:00:59 +0000 Subject: [PATCH 137/704] Made logo clickable on home page --- .../frontend/templates/html/header/logo.phtml | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml b/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml index 17f8d7c70f574..79b891b7e55e6 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml @@ -12,19 +12,11 @@ ?> <?php $storeName = $block->getThemeName() ? $block->getThemeName() : $block->getLogoAlt();?> <span data-action="toggle-nav" class="action nav-toggle"><span><?= /* @escapeNotVerified */ __('Toggle Nav') ?></span></span> -<?php if ($block->isHomePage()):?> - <strong class="logo"> -<?php else: ?> - <a class="logo" href="<?= $block->getUrl('') ?>" title="<?= /* @escapeNotVerified */ $storeName ?>"> -<?php endif ?> - <img src="<?= /* @escapeNotVerified */ $block->getLogoSrc() ?>" - title="<?= /* @escapeNotVerified */ $block->getLogoAlt() ?>" - alt="<?= /* @escapeNotVerified */ $block->getLogoAlt() ?>" - <?= $block->getLogoWidth() ? 'width="' . $block->getLogoWidth() . '"' : '' ?> - <?= $block->getLogoHeight() ? 'height="' . $block->getLogoHeight() . '"' : '' ?> - /> -<?php if ($block->isHomePage()):?> - </strong> -<?php else:?> - </a> -<?php endif?> +<a class="logo" href="<?= $block->getUrl('') ?>" title="<?= /* @escapeNotVerified */ $storeName ?>"> + <img src="<?= /* @escapeNotVerified */ $block->getLogoSrc() ?>" + title="<?= /* @escapeNotVerified */ $block->getLogoAlt() ?>" + alt="<?= /* @escapeNotVerified */ $block->getLogoAlt() ?>" + <?= $block->getLogoWidth() ? 'width="' . $block->getLogoWidth() . '"' : '' ?> + <?= $block->getLogoHeight() ? 'height="' . $block->getLogoHeight() . '"' : '' ?> + /> +</a> From cefc00668a9ccf72308c17e5a38aa8349e0d9058 Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Wed, 14 Nov 2018 11:55:58 +0200 Subject: [PATCH 138/704] MAGETWO-96209: Customer address grid actions send GET instead of POST request - Pass form key parameter for post action in ajax request; - Add comments; --- .../Address/AbstractDefaultAddress.php | 4 ++-- .../Listing/Address/Column/Actions.php | 1 - .../adminhtml/web/js/grid/columns/actions.js | 18 +++++++++++++++--- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php index 9ec38f4929a87..64062139ef0fb 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/AbstractDefaultAddress.php @@ -9,7 +9,7 @@ use Magento\Backend\App\Action; use Magento\Customer\Api\AddressRepositoryInterface; -use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\Result\JsonFactory; use Magento\Framework\Phrase; @@ -18,7 +18,7 @@ /** * Abstract class for customer default addresses changing */ -abstract class AbstractDefaultAddress extends Action implements HttpGetActionInterface +abstract class AbstractDefaultAddress extends Action implements HttpPostActionInterface { /** * Authorization level of a basic admin session diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php index 7da6d3a6a86c5..490a14169e7b7 100644 --- a/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php +++ b/app/code/Magento/Customer/Ui/Component/Listing/Address/Column/Actions.php @@ -113,7 +113,6 @@ public function prepareDataSource(array $dataSource): array ['parent_id' => $item['parent_id'], 'id' => $item['entity_id']] ), 'label' => __('Delete'), - 'post' => true, 'isAjax' => true, 'confirm' => [ 'title' => __('Delete address'), diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js b/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js index 4a62ce2e6eccf..405e3e4b0dc69 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/grid/columns/actions.js @@ -19,14 +19,18 @@ define([ defaults: { ajaxSettings: { method: 'POST', - dataType: 'json', - formKey: $('input[name="form_key"]').val() + dataType: 'json' }, listens: { action: 'onAction' } }, + /** + * Reload customer address listing data source after customer address delete action + * + * @param {Object} data + */ onAction: function (data) { if (data.action === 'delete') { this.source().reload({ @@ -66,9 +70,17 @@ define([ } }, + /** + * Send customer address listing ajax request + * + * @param {String} href + */ request: function (href) { var settings = _.extend({}, this.ajaxSettings, { - url: href + url: href, + data: { + 'form_key': window.FORM_KEY + } }); $('body').trigger('processStart'); From 7b6917c2ce5c19edc2d293a40a7c1b39b1aa1060 Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Wed, 14 Nov 2018 16:48:20 +0200 Subject: [PATCH 139/704] MAGETWO-96222: Apply Category Permissions to Website 2 --- .../Test/Mftf/ActionGroup/AdminProductActionGroup.xml | 1 - .../Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml | 1 - .../Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml | 5 +++-- app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml | 1 + app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml | 1 + 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index dce3c72106c51..4084a2871b62f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -121,7 +121,6 @@ <argument name="simpleProduct"/> </arguments> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad stepKey="waitForPageLoad"/> <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/> <click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/> <fillField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index 1bd9bb4a09c86..94400b2208b8b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -172,7 +172,6 @@ </arguments> <!--TODO use other action group for filtering grid when MQE-539 is implemented --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/> - <waitForPageLoad time="60" stepKey="waitForPageLoadInitial"/> <conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial"/> <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="openProductFilters"/> <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{sku}}" stepKey="fillProductSkuFilter"/> diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml index b21c79692a7cf..32e137c336cb3 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml @@ -10,14 +10,15 @@ <actionGroup name="AdminCreateStoreGroupActionGroup"> <arguments> <argument name="Website" defaultValue="_defaultWebsite"/> + <argument name="storeGroup" defaultValue="CustomStoreGroupCustomWebsite"/> </arguments> <amOnPage url="{{AdminSystemStoreGroupPage.url}}" stepKey="navigateToNewStoreGroup"/> <waitForPageLoad stepKey="waitForStoreGroupPageLoad" /> <comment userInput="Creating Store Group" stepKey="storeGroupCreationComment" /> <selectOption selector="{{AdminNewStoreGroupSection.storeGrpWebsiteDropdown}}" userInput="{{Website.name}}" stepKey="selectWebsite" /> - <fillField selector="{{AdminNewStoreGroupSection.storeGrpNameTextField}}" userInput="{{CustomStoreGroupCustomWebsite.name}}" stepKey="enterStoreGroupName" /> - <fillField selector="{{AdminNewStoreGroupSection.storeGrpCodeTextField}}" userInput="{{CustomStoreGroupCustomWebsite.code}}" stepKey="enterStoreGroupCode" /> + <fillField selector="{{AdminNewStoreGroupSection.storeGrpNameTextField}}" userInput="{{storeGroup.name}}" stepKey="enterStoreGroupName" /> + <fillField selector="{{AdminNewStoreGroupSection.storeGrpCodeTextField}}" userInput="{{storeGroup.code}}" stepKey="enterStoreGroupCode" /> <selectOption selector="{{AdminNewStoreGroupSection.storeRootCategoryDropdown}}" userInput="Default Category" stepKey="setRootCategory" /> <click selector="{{AdminNewStoreGroupActionsSection.saveButton}}" stepKey="clickSaveStoreGroup" /> <waitForElementVisible selector="{{AdminStoresGridSection.storeFilterTextField}}" stepKey="waitForPageReload"/> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml index 7b31623f237e9..ce8336e23fc01 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml @@ -31,4 +31,5 @@ <data key="name">NewStore</data> <data key="code">Base1</data> </entity> + <entity name="customStoreGroup2" extends="customStoreGroup" type="group"/> </entities> diff --git a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml index ee137d78d7fd2..70b018907ee99 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml @@ -19,4 +19,5 @@ <data key="store_type">website</data> <data key="website_id">null</data> </entity> + <entity name="customWebsite2" extends="customWebsite" type="website"/> </entities> From c9f4db3d2421f3b3b8b648e6c9aac129cd68330b Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov <lpoluyanov@magento.com> Date: Wed, 14 Nov 2018 16:58:11 +0200 Subject: [PATCH 140/704] MAGETWO-96312: Delete Address button on customer address modal does not close and returns json --- .../Adminhtml/Edit/Address/DeleteButton.php | 32 +++++++++-- .../Controller/Adminhtml/Address/Delete.php | 3 +- .../ui_component/customer_address_form.xml | 2 +- .../adminhtml/web/js/form/components/form.js | 53 +++++++++++++++++++ 4 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php index 8375aa13fdeff..88392139d137e 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php @@ -23,14 +23,36 @@ class DeleteButton extends GenericButton implements ButtonProviderInterface public function getButtonData() { $data = []; + $confirm = __('Are you sure you want to delete this address?'); if ($this->getAddressId()) { $data = [ 'label' => __('Delete'), - 'class' => 'delete', - 'on_click' => 'deleteConfirm(\'' . __( - 'Are you sure you want to delete this address?' - ) . '\', \'' . $this->getDeleteUrl() . '\')', - 'sort_order' => 15, + 'on_click' => '', + 'data_attribute' => [ + 'mage-init' => [ + 'Magento_Ui/js/form/button-adapter' => [ + 'actions' => [ + [ + 'targetName' => 'customer_address_form.customer_address_form', + 'actionName' => 'delete', + 'params' => [ + $this->getDeleteUrl(), + ], + + ], + [ + 'targetName' => 'customer_form.areas.address.address.customer_address_update_modal', + 'actionName' => 'closeModal' + ], + [ + 'targetName' => 'customer_address_listing.customer_address_listing', + 'actionName' => 'reload' + ] + ], + ], + ], + ], + 'sort_order' => 20 ]; } return $data; diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php index 65da3bca49e9b..d14cf86e4084e 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php @@ -9,6 +9,7 @@ use Magento\Backend\App\Action; use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\Result\JsonFactory; use Magento\Customer\Api\AddressRepositoryInterface; @@ -17,7 +18,7 @@ /** * Button for deletion of customer address in admin */ -class Delete extends Action implements HttpGetActionInterface +class Delete extends Action implements HttpPostActionInterface { /** * Authorization level of a basic admin session diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index 4aeb7d67ace68..8e61c458fb812 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -5,7 +5,7 @@ * See COPYING.txt for license details. */ --> -<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd"> +<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd" component="Magento_Customer/js/form/components/form"> <argument name="data" xsi:type="array"> <item name="js_config" xsi:type="array"> <item name="provider" xsi:type="string">customer_address_form.customer_address_form_data_source</item> diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js new file mode 100644 index 0000000000000..893388a869090 --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js @@ -0,0 +1,53 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'jquery', + 'Magento_Ui/js/modal/alert', + 'Magento_Ui/js/form/form', + 'mage/translate' +], function ($, uiAlert, Form, $t) { + 'use strict'; + + return Form.extend({ + defaults: { + ajaxSettings: { + method: 'POST', + dataType: 'json' + } + }, + + /** + * Perform asynchronous DELETE request to server. + * @param {String} url - ajax url + * @returns {Deferred} + */ + delete: function (url) { + var settings = _.extend({}, this.ajaxSettings, { + url: url, + data: { + 'form_key': window.FORM_KEY + } + }); + + return $.ajax(settings) + .done(function (response) { + if (response.error) { + uiAlert({ + content: response.message + }); + } + }) + .fail(function () { + uiAlert({ + content: $t('Sorry, there has been an error processing your request. Please try again later.') + }); + }) + .always(function () { + $('body').trigger('processStop'); + }); + + } + }); +}); From 62fa0e82be34ad04bc0cc4308fd41bbd32ba34c6 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 14 Nov 2018 09:50:24 -0600 Subject: [PATCH 141/704] MAGETWO-93181: Grouped product doesn't take care about his Linked Products when SalableQuantity < ProductLink.ExtensionAttributes.Qty after Source Deduction - converted change to be backwards compatible --- .../Model/Product/Type/Grouped.php | 94 +++++++++++++------ 1 file changed, 65 insertions(+), 29 deletions(-) diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php index 2f8249397819a..8fc0b856ca46d 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php @@ -10,6 +10,7 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\App\ObjectManager; use Magento\CatalogInventory\Helper\Stock as StockHelper; +use \Magento\Catalog\Model\Product; /** * Grouped product type model @@ -29,6 +30,13 @@ class Grouped extends \Magento\Catalog\Model\Product\Type\AbstractType */ protected $_keyAssociatedProducts = '_cache_instance_associated_products'; + /** + * Cache key for Associated Products that are in stock + * + * @var string + */ + protected $_keyInStockAssociatedProducts = '_cache_instance_in_stock_associated_products'; + /** * Cache key for Associated Product Ids * @@ -205,39 +213,15 @@ public function getParentIdsByChild($childId) } /** - * Retrieve array of associated products + * Retrieve array of associated products excluding those that are out of stock * * @param \Magento\Catalog\Model\Product $product - * @param bool $includeOutOfStock * @return array */ - public function getAssociatedProducts($product, bool $includeOutOfStock = true) + public function getAssociatedProducts($product) { if (!$product->hasData($this->_keyAssociatedProducts)) { - $associatedProducts = []; - - $this->setSaleableStatus($product); - - $collection = $this->getAssociatedProductCollection( - $product - )->addAttributeToSelect( - ['name', 'price', 'special_price', 'special_from_date', 'special_to_date', 'tax_class_id'] - )->addFilterByRequiredOptions()->setPositionOrder()->addStoreFilter( - $this->getStoreFilter($product) - )->addAttributeToFilter( - 'status', - ['in' => $this->getStatusFilters($product)] - ); - - if (!$includeOutOfStock) { - $this->stockHelper->addIsInStockFilterToCollection($collection); - } - - foreach ($collection as $item) { - $associatedProducts[] = $item; - } - - $product->setData($this->_keyAssociatedProducts, $associatedProducts); + $this->assignAssociatedProducts($product, true); } return $product->getData($this->_keyAssociatedProducts); } @@ -352,7 +336,9 @@ public function getAssociatedProductCollection($product) protected function getProductInfo(\Magento\Framework\DataObject $buyRequest, $product, $isStrictProcessMode) { $productsInfo = $buyRequest->getSuperGroup() ?: []; - $associatedProducts = $this->getAssociatedProducts($product, !empty($productsInfo)); + $associatedProducts = empty($productsInfo) + ? $this->getInStockAssociatedProducts($product) + : $this->getAssociatedProducts($product); if (!is_array($productsInfo)) { return __('Please specify the quantity of product(s).')->render(); @@ -391,7 +377,7 @@ protected function _prepareProduct(\Magento\Framework\DataObject $buyRequest, $p return $productsInfo; } $associatedProducts = !$isStrictProcessMode || !empty($productsInfo) - ? $this->getAssociatedProducts($product) + ? $this->getInStockAssociatedProducts($product) : false; foreach ($associatedProducts as $subProduct) { @@ -524,4 +510,54 @@ public function getChildrenMsrp(\Magento\Catalog\Model\Product $product) } return $prices ? min($prices) : 0; } + + /** + * Retrieve array of associated products that are out in stock + * + * @param \Magento\Catalog\Model\Product $product + * @return array + */ + private function getInStockAssociatedProducts($product) + { + if (!$product->hasData($this->_keyInStockAssociatedProducts)) { + $this->assignAssociatedProducts($product, false); + } + return $product->getData($this->_keyInStockAssociatedProducts); + } + + /** + * Caches associated products to the product optionally including out of stock items + * + * @param Product $product + * @param bool $includeOutOfStock + */ + private function assignAssociatedProducts(Product $product, bool $includeOutOfStock = true): void + { + $associatedProducts = []; + + $this->setSaleableStatus($product); + + $collection = $this->getAssociatedProductCollection( + $product + )->addAttributeToSelect( + ['name', 'price', 'special_price', 'special_from_date', 'special_to_date', 'tax_class_id'] + )->addFilterByRequiredOptions()->setPositionOrder()->addStoreFilter( + $this->getStoreFilter($product) + )->addAttributeToFilter( + 'status', + ['in' => $this->getStatusFilters($product)] + ); + + if (!$includeOutOfStock) { + $this->stockHelper->addIsInStockFilterToCollection($collection); + } + + foreach ($collection as $item) { + $associatedProducts[] = $item; + } + + $cacheKey = $includeOutOfStock ? $this->_keyAssociatedProducts : $this->_keyInStockAssociatedProducts; + + $product->setData($cacheKey, $associatedProducts); + } } From ea3f224f63215e9ad1d109cae71243a043cb0b52 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 14 Nov 2018 09:56:26 -0600 Subject: [PATCH 142/704] MAGETWO-93181: Grouped product doesn't take care about his Linked Products when SalableQuantity < ProductLink.ExtensionAttributes.Qty after Source Deduction - Fixed description --- app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php index 8fc0b856ca46d..399309c9c2f19 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php @@ -213,7 +213,7 @@ public function getParentIdsByChild($childId) } /** - * Retrieve array of associated products excluding those that are out of stock + * Retrieve array of associated products including those that are out of stock * * @param \Magento\Catalog\Model\Product $product * @return array From e9e2551c43ff513d28350ba4dbeb7c89f036b781 Mon Sep 17 00:00:00 2001 From: Oleksandr Miroshnichenko <omiroshnichenko@magento.com> Date: Wed, 14 Nov 2018 10:39:47 -0600 Subject: [PATCH 143/704] MAGETWO-96312: Delete Address button on customer address modal does not close and returns json --- .../Adminhtml/Edit/Address/DeleteButton.php | 10 +-- .../ui_component/customer_address_form.xml | 3 + .../adminhtml/web/js/form/components/form.js | 69 ++++++++++++------- .../web/js/form/components/insert-form.js | 8 +++ .../view/base/ui_component/customer_form.xml | 1 + 5 files changed, 58 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php index 88392139d137e..7296a5167622a 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php @@ -34,19 +34,11 @@ public function getButtonData() 'actions' => [ [ 'targetName' => 'customer_address_form.customer_address_form', - 'actionName' => 'delete', + 'actionName' => 'deleteAddress', 'params' => [ $this->getDeleteUrl(), ], - ], - [ - 'targetName' => 'customer_form.areas.address.address.customer_address_update_modal', - 'actionName' => 'closeModal' - ], - [ - 'targetName' => 'customer_address_listing.customer_address_listing', - 'actionName' => 'reload' ] ], ], diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index 8e61c458fb812..afe2396a9062f 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -10,6 +10,9 @@ <item name="js_config" xsi:type="array"> <item name="provider" xsi:type="string">customer_address_form.customer_address_form_data_source</item> </item> + <item name="config" xsi:type="array"> + <item name="confirmationMessage" translate="true" xsi:type="string">Are you sure you want to delete this address?</item> + </item> <item name="label" xsi:type="string" translate="true">Update Address</item> <item name="reverseMetadataMerge" xsi:type="boolean">true</item> <item name="template" xsi:type="string">templates/form/collapsible</item> diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js index 893388a869090..34cbbfefce368 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js @@ -5,48 +5,69 @@ define([ 'jquery', 'Magento_Ui/js/modal/alert', + 'Magento_Ui/js/modal/confirm', 'Magento_Ui/js/form/form', 'mage/translate' -], function ($, uiAlert, Form, $t) { +], function ($, uiAlert, uiConfirm, Form, $t) { 'use strict'; return Form.extend({ defaults: { + confirmationMessage: '', ajaxSettings: { - method: 'POST', - dataType: 'json' + method: 'POST', + dataType: 'json' } }, + deleteAddress: function (url) { + var that = this; + + uiConfirm({ + content: this.confirmationMessage, + actions: { + /** @inheritdoc */ + confirm: function () { + that._delete(url); + } + } + }); + }, + /** * Perform asynchronous DELETE request to server. * @param {String} url - ajax url * @returns {Deferred} */ - delete: function (url) { + _delete: function (url) { var settings = _.extend({}, this.ajaxSettings, { - url: url, - data: { - 'form_key': window.FORM_KEY - } - }); + url: url, + data: { + 'form_key': window.FORM_KEY + } + }), + that = this; + + $('body').trigger('processStart'); return $.ajax(settings) - .done(function (response) { - if (response.error) { - uiAlert({ - content: response.message - }); - } - }) - .fail(function () { - uiAlert({ - content: $t('Sorry, there has been an error processing your request. Please try again later.') - }); - }) - .always(function () { - $('body').trigger('processStop'); - }); + .done(function (response) { + if (response.error) { + uiAlert({ + content: response.message + }); + } else { + that.trigger('deleteAddressAction', that.source.get('data.entity_id')); + } + }) + .fail(function () { + uiAlert({ + content: $t('Sorry, there has been an error processing your request. Please try again later.') + }); + }) + .always(function () { + $('body').trigger('processStop'); + }); } }); diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js index 25303899894c4..59c3e8e4b8b0b 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js @@ -50,6 +50,14 @@ define([ ) { this.source.set('data.default_shipping_address', []); } + }, + + onAddressDelete: function (id) { + this.addressModal().closeModal(); + this.addressListing().reload({ + refresh: true + }); + this.addressListing()._delete([parseFloat(id)]); } }); }); diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index d6d61c892e00d..a7c55da2cca2f 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -467,6 +467,7 @@ </exports> <imports> <link name="parentId">${ $.provider}:data.customer_id</link> + <link name="onAddressDelete">${ $.ns }.${ $.ns }:deleteAddressAction</link> </imports> </settings> </insertForm> From b05611c12ee10bfc2268a1dd14398fe756dc8a04 Mon Sep 17 00:00:00 2001 From: Nathan Smith <nathsmit@adobe.com> Date: Wed, 14 Nov 2018 12:07:28 -0600 Subject: [PATCH 144/704] MAGETWO-93181: Grouped product doesn't take care about his Linked Products when SalableQuantity < ProductLink.ExtensionAttributes.Qty after Source Deduction - Marked new property as private --- .../GroupedProduct/Model/Product/Type/Grouped.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php index 399309c9c2f19..0bbadaa1ba869 100644 --- a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php +++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php @@ -30,13 +30,6 @@ class Grouped extends \Magento\Catalog\Model\Product\Type\AbstractType */ protected $_keyAssociatedProducts = '_cache_instance_associated_products'; - /** - * Cache key for Associated Products that are in stock - * - * @var string - */ - protected $_keyInStockAssociatedProducts = '_cache_instance_in_stock_associated_products'; - /** * Cache key for Associated Product Ids * @@ -101,6 +94,13 @@ class Grouped extends \Magento\Catalog\Model\Product\Type\AbstractType */ private $stockHelper; + /** + * Cache key for Associated Products that are in stock + * + * @var string + */ + private $_keyInStockAssociatedProducts = '_cache_instance_in_stock_associated_products'; + /** * @param \Magento\Catalog\Model\Product\Option $catalogProductOption * @param \Magento\Eav\Model\Config $eavConfig From bd5456f00d2458c5a9f1f6a6446bec66f3ea7f2e Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Thu, 15 Nov 2018 08:49:48 +0200 Subject: [PATCH 145/704] MAGETWO-96222: Apply Category Permissions to Website 2 --- ...upActionGroup.xml => AdminStoreGroupCreateActionGroup.xml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename app/code/Magento/Store/Test/Mftf/ActionGroup/{AdminCreateStoreGroupActionGroup.xml => AdminStoreGroupCreateActionGroup.xml} (91%) diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminStoreGroupCreateActionGroup.xml similarity index 91% rename from app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml rename to app/code/Magento/Store/Test/Mftf/ActionGroup/AdminStoreGroupCreateActionGroup.xml index 32e137c336cb3..023d5fc3587fb 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreGroupActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminStoreGroupCreateActionGroup.xml @@ -7,10 +7,10 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminCreateStoreGroupActionGroup"> + <actionGroup name="AdminStoreGroupCreateActionGroup"> <arguments> <argument name="Website" defaultValue="_defaultWebsite"/> - <argument name="storeGroup" defaultValue="CustomStoreGroupCustomWebsite"/> + <argument name="storeGroup"/> </arguments> <amOnPage url="{{AdminSystemStoreGroupPage.url}}" stepKey="navigateToNewStoreGroup"/> <waitForPageLoad stepKey="waitForStoreGroupPageLoad" /> From 0419965f475f2e72a819a545f55ef51a000ec9b1 Mon Sep 17 00:00:00 2001 From: DianaRusin <rusind95@gmail.com> Date: Thu, 15 Nov 2018 10:13:59 +0200 Subject: [PATCH 146/704] MAGETWO-95576: [FT][Amazon] Tests fail when Amazon is configured --- .../BraintreeCreditCardOnCheckoutTest.xml | 2 +- ...ctWithCustomOptionsWithLongValuesTitle.xml | 3 ++ ...tedProductToConfigurableOutOfStockTest.xml | 2 ++ .../Mftf/ActionGroup/CheckoutActionGroup.xml | 12 +++++++ ...eckoutFillNewBillingAddressActionGroup.xml | 9 ++++- .../Mftf/Section/CheckoutPaymentSection.xml | 3 +- .../Test/CheckCheckoutSuccessPageTest.xml | 17 +++++++--- .../Test/StorefrontCustomerCheckoutTest.xml | 11 +++--- .../Mftf/Test/StorefrontGuestCheckoutTest.xml | 3 +- .../ActionGroup/AdminOrderActionGroup.xml | 34 ++++++++++++++++++- .../CreateOrderToPrintPageActionGroup.xml | 4 +++ .../Test/Mftf/Page/AdminOrderCreatePage.xml | 3 +- .../Section/AdminOrderFormPaymentSection.xml | 2 ++ .../AdminOrderFormStoreSelectorSection.xml | 15 ++++++++ ...inAbleToShipPartiallyInvoicedItemsTest.xml | 5 +++ .../Test/Mftf/Test/AdminCreateInvoiceTest.xml | 2 ++ ...reateOrderWithMinimumAmountEnabledTest.xml | 8 +++-- ...dminSubmitConfigurableProductOrderTest.xml | 5 ++- ...minSubmitsOrderWithAndWithoutEmailTest.xml | 5 ++- ...editMemoTotalAfterShippingDiscountTest.xml | 4 +++ .../Test/StorefrontRedirectToOrderHistory.xml | 4 +-- .../StorefrontSalesRuleActionGroup.xml | 3 +- .../StorefrontAutoGeneratedCouponCodeTest.xml | 6 ++-- .../Test/Mftf/Test/AdminTaxReportGridTest.xml | 5 +-- .../Test/StorefrontTaxQuoteCheckoutTest.xml | 11 +++++- 25 files changed, 152 insertions(+), 26 deletions(-) create mode 100644 app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormStoreSelectorSection.xml diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml b/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml index 2b5d60bea24e1..f27477ce8a672 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml @@ -65,7 +65,7 @@ <actionGroup ref="StorefrontFillCartDataActionGroup" stepKey="StorefrontFillCartDataActionGroup"/> <waitForPageLoad stepKey="waitForPageLoad4"/> <!--Place order--> - <click selector="{{CheckoutPaymentSection.placeOrder}}" + <click selector="{{BraintreeConfigurationPaymentSection.paymentMethodContainer}}{{CheckoutPaymentSection.placeOrder}}" stepKey="PlaceOrder"/> <waitForPageLoad stepKey="waitForPageLoad5"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml index fcdb92d71f1f8..42ab38086c0cb 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsWithLongValuesTitle.xml @@ -78,6 +78,9 @@ <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterClickNext"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> + <!-- Place Order --> <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectPaymentMethod"/> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml index 534541c96ea8c..a12e8a5ed2cb3 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml @@ -104,6 +104,8 @@ <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton"/> <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> <waitForPageLoad stepKey="waitForOrderSuccessPage1"/> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml index 052eba413d0b2..90df06e4783e7 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml @@ -43,6 +43,11 @@ <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> </actionGroup> + <actionGroup name="GuestCheckoutFillShippingNoWaitForPaymentActionGroup" extends="GuestCheckoutFillingShippingSectionActionGroup"> + <remove keyForRemoval="waitForPaymentSectionLoaded"/> + <remove keyForRemoval="assertCheckoutPaymentUrl"/> + </actionGroup> + <!-- Guest checkout filling shipping section without region --> <actionGroup name="GuestCheckoutFillingShippingSectionWithoutRegionActionGroup"> <arguments> @@ -88,6 +93,13 @@ <see userInput="No Payment method available." stepKey="checkMessage"/> </actionGroup> + <actionGroup name="GuestCheckoutWithSpecificCountryOptionForPaymentMethodActionGroup" extends="GuestCheckoutFillingShippingSectionUnavailablePaymentActionGroup"> + <arguments> + <argument name="paymentMethod" type="string"/> + </arguments> + <remove keyForRemoval="checkMessage"/> + <dontsee selector="{{CheckoutPaymentSection.paymentMethodByName(paymentMethod)}}" parametrized="true" stepKey="paymentMethodDoesNotAvailable"/> + </actionGroup> <!-- Logged in user checkout filling shipping section --> <actionGroup name="LoggedInUserCheckoutFillingShippingSectionActionGroup"> <arguments> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml index 62dd6b67b78a6..34f2cfe7f7fff 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml @@ -65,4 +65,11 @@ <selectOption selector="{{classPrefix}} {{CheckoutShippingSection.country}}" userInput="" stepKey="clearFieldCounty"/> <clearField selector="{{classPrefix}} {{CheckoutShippingSection.telephone}}" stepKey="clearFieldPhoneNumber"/> </actionGroup> -</actionGroups> \ No newline at end of file + <actionGroup name="GuestCheckoutSelectPaymentAndFillNewBillingAddressActionGroup" extends="GuestCheckoutFillNewBillingAddressActionGroup"> + <arguments> + <argument name="paymentMethod" type="string"/> + </arguments> + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" after="waitForLoading3" stepKey="waitForPaymentSectionLoaded"/> + <conditionalClick selector="{{CheckoutPaymentSection.paymentMethodByName(paymentMethod)}}" dependentSelector="{{CheckoutPaymentSection.billingAddress}}" visible="false" parametrized="true" before="enterFirstName" stepKey="clickCheckMoneyOrderPayment"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml index 70bb0532222a4..0499c01f8840c 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutPaymentSection.xml @@ -22,7 +22,7 @@ <element name="guestRegion" type="select" selector=".billing-address-form select[name*='region_id']"/> <element name="guestPostcode" type="input" selector=".billing-address-form input[name*='postcode']"/> <element name="guestTelephone" type="input" selector=".billing-address-form input[name*='telephone']"/> - <element name="billingAddress" type="text" selector="div.billing-address-details"/> + <element name="billingAddress" type="text" selector=".payment-method._active div.billing-address-details"/> <element name="cartItems" type="text" selector="ol.minicart-items"/> <element name="cartItemsArea" type="button" selector="div.block.items-in-cart"/> <element name="cartItemsAreaActive" type="textarea" selector="div.block.items-in-cart.active" timeout="30"/> @@ -52,5 +52,6 @@ <element name="addressAction" type="button" selector="//span[text()='{{action}}']" parameterized="true"/> <element name="addressBook" type="button" selector="//a[text()='Address Book']"/> <element name="noQuotes" type="text" selector=".no-quotes-block"/> + <element name="paymentMethodByName" type="text" selector="//*[@id='checkout-payment-method-load']//*[contains(@class, 'payment-group')]//label[normalize-space(.)='{{var1}}']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml index a41b1afc74368..e19627e7435d6 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml @@ -55,8 +55,8 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask2"/> <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton"/> <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext"/> - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoadedTest3"/> - + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> <!--Click Place Order button--> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="seeSuccessTitle"/> @@ -81,7 +81,8 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask3"/> <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton2"/> <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext2"/> - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoadedTest4"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> <!--Click Place Order button--> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder2"/> @@ -105,7 +106,9 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask4"/> <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton3"/> <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext3"/> - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" stepKey="waitForPaymentSectionLoadedTest5"/> + + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment3"/> <!--Click Place Order button--> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder3"/> @@ -165,6 +168,9 @@ <argument name="customerAddressVar" value="CustomerAddressSimple" /> </actionGroup> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> + <!--Click Place Order button--> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage"/> @@ -198,6 +204,9 @@ <argument name="customerAddressVar" value="CustomerAddressSimple" /> </actionGroup> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> + <!--Click Place Order button--> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder2"/> <see selector="{{CheckoutSuccessMainSection.successTitle}}" userInput="Thank you for your purchase!" stepKey="waitForLoadSuccessPage2"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml index 1ef7403e94ce1..610a4a7cfd299 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml @@ -48,6 +48,8 @@ <click stepKey="s35" selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}"/> <waitForElement stepKey="s36" selector="{{CheckoutShippingMethodsSection.next}}" time="30"/> <click stepKey="s37" selector="{{CheckoutShippingMethodsSection.next}}" /> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> <waitForPageLoad stepKey="s39"/> <waitForElement stepKey="s41" selector="{{CheckoutPaymentSection.placeOrder}}" time="30" /> <see stepKey="s47" selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" /> @@ -163,7 +165,8 @@ <click stepKey="selectFirstShippingMethod1" selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}"/> <waitForElement stepKey="waitForShippingMethodSelect1" selector="{{CheckoutShippingMethodsSection.next}}" time="30"/> <click stepKey="clickNextOnShippingMethodLoad1" selector="{{CheckoutShippingMethodsSection.next}}" /> - <waitForPageLoad stepKey="waitForPaymentLoad1"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> <waitForElement stepKey="waitForPlaceOrderButton1" selector="{{CheckoutPaymentSection.placeOrder}}" time="30" /> <see stepKey="seeBillingAddressIsCorrect1" selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{US_Address_NY.street[0]}}" /> <click stepKey="clickPlaceOrderButton1" selector="{{CheckoutPaymentSection.placeOrder}}" /> @@ -186,7 +189,8 @@ <click stepKey="selectFirstShippingMethod2" selector="{{CheckoutShippingMethodsSection.firstShippingMethod}}"/> <waitForElement stepKey="waitForShippingMethodSelect2" selector="{{CheckoutShippingMethodsSection.next}}" time="30"/> <click stepKey="clickNextOnShippingMethodLoad2" selector="{{CheckoutShippingMethodsSection.next}}" /> - <waitForPageLoad stepKey="waitForPaymentLoad2"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> <waitForElement stepKey="waitForPlaceOrderButton2" selector="{{CheckoutPaymentSection.placeOrder}}" time="30" /> <see stepKey="seeBillingAddressIsCorrect2" selector="{{CheckoutPaymentSection.billingAddress}}" userInput="{{UK_Not_Default_Address.street[0]}}" /> <click stepKey="clickPlaceOrderButton2" selector="{{CheckoutPaymentSection.placeOrder}}" /> @@ -242,8 +246,7 @@ <click stepKey="clickNextButton" selector="{{CheckoutShippingMethodsSection.next}}" /> <waitForPageLoad stepKey="waitBillingForm"/> <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> - <waitForElementVisible selector="{{CheckoutPaymentSection.noQuotes}}" stepKey="waitMessage"/> - <see userInput="No Payment method available." stepKey="checkMessage"/> + <dontsee selector="{{CheckoutPaymentSection.paymentMethodByName('Check / Money order')}}" stepKey="paymentMethodDoesNotAvailable"/> <!-- Fill UK Address and verify that payment available and checkout successful --> <click selector="{{CheckoutHeaderSection.shippingMethodStep}}" stepKey="goToShipping" /> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml index f9533fd946f35..21200436992d5 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml @@ -109,9 +109,10 @@ <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromMinicart" /> <!-- Fill US Address and verify that no payment available --> - <actionGroup ref="GuestCheckoutFillingShippingSectionUnavailablePaymentActionGroup" stepKey="guestCheckoutFillingShippingSection"> + <actionGroup ref="GuestCheckoutWithSpecificCountryOptionForPaymentMethodActionGroup" stepKey="guestCheckoutFillingShippingSection"> <argument name="customerVar" value="CustomerEntityOne" /> <argument name="customerAddressVar" value="CustomerAddressSimple" /> + <argument name="paymentMethod" value="Check / Money order"/> </actionGroup> <!-- Fill UK Address and verify that payment available and checkout successful --> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index cc8a62ca48961..8bfe1eb6c38cb 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -311,6 +311,32 @@ <see stepKey="seeSuccessMessageForOrder" userInput="You created the order."/> </actionGroup> + <actionGroup name="CreateOrderInStoreActionGroup"> + <arguments> + <argument name="product"/> + <argument name="customer"/> + <argument name="storeView"/> + </arguments> + <amOnPage stepKey="navigateToNewOrderPage" url="{{AdminOrderCreatePage.url}}"/> + <click stepKey="chooseCustomer" selector="{{AdminOrdersGridSection.customerInOrdersSection(customer.firstname)}}"/> + <waitForPageLoad stepKey="waitForStoresPageOpened"/> + <click stepKey="chooseStore" selector="{{AdminOrderStoreScopeTreeSection.storeForOrder(storeView.name)}}"/> + <scrollToTopOfPage stepKey="scrollToTop"/> + <click selector="{{OrdersGridSection.addProducts}}" stepKey="clickOnAddProducts"/> + <waitForPageLoad stepKey="waitForProductsListForOrder"/> + <click selector="{{AdminOrdersGridSection.productForOrder(product.sku)}}" stepKey="chooseTheProduct"/> + <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="addSelectedProductToOrder"/> + <waitForPageLoad stepKey="waitForProductAddedInOrder"/> + <click selector="{{AdminInvoicePaymentShippingSection.getShippingMethodAndRates}}" stepKey="openShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethods"/> + <click selector="{{AdminInvoicePaymentShippingSection.shippingMethod}}" stepKey="chooseShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethodsThickened"/> + <waitForElementVisible selector="{{AdminOrderFormPaymentSection.paymentBlock}}" stepKey="waitForPaymentOptions"/> + <conditionalClick selector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" dependentSelector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" visible="true" stepKey="checkCheckMoneyOption"/> + <click selector="{{OrdersGridSection.submitOrder}}" stepKey="submitOrder"/> + <see stepKey="seeSuccessMessageForOrder" userInput="You created the order."/> + </actionGroup> + <!--Cancel order that is in pending status--> <actionGroup name="cancelPendingOrder"> <click selector="{{AdminOrderDetailsMainActionsSection.cancel}}" stepKey="clickCancelOrder"/> @@ -320,4 +346,10 @@ <see selector="{{AdminMessagesSection.success}}" userInput="You canceled the order." stepKey="seeCancelSuccessMessage"/> <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Canceled" stepKey="seeOrderStatusCanceled"/> </actionGroup> -</actionGroups> \ No newline at end of file + + <!--Select Check Money payment method--> + <actionGroup name="SelectCheckMoneyPaymentMethod"> + <waitForElementVisible selector="{{AdminOrderFormPaymentSection.paymentBlock}}" stepKey="waitForPaymentOptions"/> + <conditionalClick selector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" dependentSelector="{{AdminOrderFormPaymentSection.checkMoneyOption}}" visible="true" stepKey="checkCheckMoneyOption"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderToPrintPageActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderToPrintPageActionGroup.xml index fe2fc49f1d015..e48e7bb3f0b1e 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderToPrintPageActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/CreateOrderToPrintPageActionGroup.xml @@ -30,4 +30,8 @@ <click selector="{{CheckoutSuccessMainSection.orderLink}}" stepKey="clickOrderLink"/> <click selector="{{StorefrontCustomerOrderViewSection.printOrderLink}}" stepKey="clickPrintOrderLink"/> </actionGroup> + <actionGroup name="CreateOrderToPrintPageWithSelectedPaymentMethodActionGroup" extends="CreateOrderToPrintPageActionGroup"> + <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" after="clickNext" stepKey="waitForPaymentSectionLoaded"/> + <conditionalClick selector="{{CheckoutPaymentSection.checkMoneyOrderPayment}}" dependentSelector="{{CheckoutPaymentSection.billingAddress}}" visible="false" before="waitForPlaceOrderButton" stepKey="clickCheckMoneyOrderPayment"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml index 333be23dbf346..c8ec12203c676 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml @@ -17,5 +17,6 @@ <section name="AdminOrderFormShippingAddressSection"/> <section name="AdminOrderFormPaymentSection"/> <section name="AdminOrderFormTotalSection"/> + <section name="AdminOrderFormStoreSelectorSection"/> </page> -</pages> \ No newline at end of file +</pages> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml index 60d4c53418dc8..46a79217b08fb 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml @@ -14,5 +14,7 @@ <element name="flatRateOption" type="radio" selector="#s_method_flatrate_flatrate" timeout="30"/> <element name="shippingError" type="text" selector="#order[has_shipping]-error"/> <element name="freeShippingOption" type="radio" selector="#s_method_freeshipping_freeshipping" timeout="30"/> + <element name="checkMoneyOption" type="radio" selector="#p_method_checkmo" timeout="30"/> + <element name="paymentBlock" type="text" selector="#order-billing_method" /> </section> </sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormStoreSelectorSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormStoreSelectorSection.xml new file mode 100644 index 0000000000000..8602c0d3c608f --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormStoreSelectorSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminOrderFormStoreSelectorSection"> + <element name="storeSelectorContainer" type="input" selector="#order-store-selector"/> + <element name="defaultStoreViewButton" type="radio" selector="//*[contains(@class,'tree-store-scope')]//label[contains(text(),'Default Store View')]"/> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml index e4fd894f608c5..22532ff43fcc6 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAbleToShipPartiallyInvoicedItemsTest.xml @@ -39,6 +39,8 @@ <click selector="{{AdminOrdersGridSection.createNewOrder}}" stepKey="clickCreateNewOrder"/> <click selector="{{AdminOrderFormActionSection.CreateNewCustomer}}" stepKey="clickCreateCustomer"/> <see selector="{{AdminHeaderSection.pageTitle}}" userInput="Create New Order" stepKey="seeNewOrderPageTitle"/> + <conditionalClick selector="{{AdminOrderFormStoreSelectorSection.defaultStoreViewButton}}" dependentSelector="{{AdminOrderFormStoreSelectorSection.storeSelectorContainer}}" visible="true" stepKey="selectFirstStoreViewIfAppears"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskDisappearedAfterStoreSelected"/> <actionGroup ref="addSimpleProductToOrderWithCustomQuantity" stepKey="addSimpleProductToOrderWithUserDefinedQty"> <argument name="product" value="_defaultProduct"/> <argument name="quantity" value="10"/> @@ -56,6 +58,9 @@ <!-- Select shipping --> <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping" after="fillCustomerAddress"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="SelectCheckMoneyPaymentMethod" stepKey="selectCheckMoneyPayment"/> + <!--Verify totals on Order page--> <see selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" userInput="$1,230.00" stepKey="seeOrderSubTotal" after="selectFlatRateShipping"/> <see selector="{{AdminOrderFormTotalSection.total('Shipping')}}" userInput="$50.00" stepKey="seeOrderShipping" after="seeOrderSubTotal"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml index 24266b5bcfe9f..94e99d25dbb60 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateInvoiceTest.xml @@ -52,6 +52,8 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask2"/> <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton"/> <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml index 0b737ed459f3b..40b26a6b46045 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithMinimumAmountEnabledTest.xml @@ -38,7 +38,8 @@ <!--Admin creates order--> <comment userInput="Admin creates order" stepKey="adminCreateOrder"/> <actionGroup ref="navigateToNewOrderPageNewCustomerSingleStore" stepKey="navigateToNewOrderPage"/> - + <conditionalClick selector="{{AdminOrderFormStoreSelectorSection.defaultStoreViewButton}}" dependentSelector="{{AdminOrderFormStoreSelectorSection.storeSelectorContainer}}" visible="true" stepKey="selectFirstStoreViewIfAppears"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskDisappearedAfterStoreSelected"/> <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder"> <argument name="product" value="SimpleProduct"/> </actionGroup> @@ -55,6 +56,9 @@ <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="SelectCheckMoneyPaymentMethod" stepKey="selectCheckMoneyPayment"/> + <!--Verify totals on Order page--> <see selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" userInput="${{AdminOrderSimpleProduct.subtotal}}" stepKey="seeOrderSubTotal"/> <see selector="{{AdminOrderFormTotalSection.total('Shipping')}}" userInput="${{AdminOrderSimpleProduct.shipping}}" stepKey="seeOrderShipping"/> @@ -77,4 +81,4 @@ <argument name="product" value="SimpleProduct"/> </actionGroup> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml index ff1e27a2efa08..041252af0ac5b 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml @@ -111,6 +111,9 @@ <!--Select FlatRate shipping method--> <actionGroup ref="orderSelectFlatRateShipping" stepKey="orderSelectFlatRateShippingMethod"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="SelectCheckMoneyPaymentMethod" stepKey="selectCheckMoneyPayment"/> + <!--Submit order--> <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> @@ -129,4 +132,4 @@ <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> </after> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml index cc69b6dfb7d41..ed536bd3351f9 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitsOrderWithAndWithoutEmailTest.xml @@ -58,6 +58,9 @@ <!-- Select shipping --> <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping" after="fillCustomerAddress"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="SelectCheckMoneyPaymentMethod" stepKey="selectCheckMoneyPayment"/> + <!--Verify totals on Order page--> <see selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" userInput="${{AdminOrderSimpleProduct.subtotal}}" stepKey="seeOrderSubTotal" after="selectFlatRateShipping"/> <see selector="{{AdminOrderFormTotalSection.total('Shipping')}}" userInput="${{AdminOrderSimpleProduct.shipping}}" stepKey="seeOrderShipping" after="seeOrderSubTotal"/> @@ -69,4 +72,4 @@ <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPage" after="clickSubmitOrder"/> <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage" after="seeViewOrderPage"/> </test> - </tests> \ No newline at end of file + </tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml index 08e859b11d1bb..d8ab034b6475c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreditMemoTotalAfterShippingDiscountTest.xml @@ -85,6 +85,10 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask2"/> <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton"/> <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext"/> + + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment3"/> + <waitForElement selector="{{CheckoutPaymentSection.placeOrder}}" time="30" stepKey="waitForPlaceOrderButton"/> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontRedirectToOrderHistory.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontRedirectToOrderHistory.xml index 19bcca985f974..9790b5dfc47f3 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontRedirectToOrderHistory.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontRedirectToOrderHistory.xml @@ -40,7 +40,7 @@ </actionGroup> <!--Create an order at Storefront as Customer 1 --> - <actionGroup ref="CreateOrderToPrintPageActionGroup" stepKey="createOrderToPrint"> + <actionGroup ref="CreateOrderToPrintPageWithSelectedPaymentMethodActionGroup" stepKey="createOrderToPrint"> <argument name="Category" value="$$createCategory$$"/> </actionGroup> @@ -64,7 +64,7 @@ </actionGroup> <!--Create an order at Storefront as Customer 2 --> - <actionGroup ref="CreateOrderToPrintPageActionGroup" stepKey="createOrderToPrint2"> + <actionGroup ref="CreateOrderToPrintPageWithSelectedPaymentMethodActionGroup" stepKey="createOrderToPrint2"> <argument name="Category" value="$$createCategory$$"/> </actionGroup> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml index 70d1fc56d2cea..b7a8e2c9c68ae 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml @@ -18,6 +18,7 @@ <waitForElementVisible selector="{{StorefrontSalesRuleCartCouponSection.couponField}}" stepKey="waitForCouponField" /> <fillField userInput="{{coupon.code}}" selector="{{StorefrontSalesRuleCartCouponSection.couponField}}" stepKey="fillCouponField"/> <click selector="{{StorefrontSalesRuleCartCouponSection.applyButton}}" stepKey="clickApplyButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> </actionGroup> <!-- Cancel Sales Rule Coupon applied to the cart --> @@ -55,4 +56,4 @@ <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> <see selector="{{CheckoutCartSummarySection.discountAmount}}" userInput="{{expectedDiscount}}" stepKey="seeDiscountTotal"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml index 6f9474278bf45..ed05f8b27e5ca 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml @@ -86,8 +86,8 @@ <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> <waitForElement selector="{{CheckoutShippingMethodsSection.next}}" time="30" stepKey="waitForNextButton"/> <click selector="{{CheckoutShippingMethodsSection.next}}" stepKey="clickNext"/> - <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" - stepKey="waitForPaymentSectionLoaded"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> <waitForElement selector="{{CheckoutSuccessMainSection.success}}" time="30" stepKey="waitForLoadSuccessPage"/> @@ -126,6 +126,8 @@ <argument name="customerVar" value="CustomerEntityOne"/> <argument name="customerAddressVar" value="CustomerAddressSimple"/> </actionGroup> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder1"/> <waitForElement selector="{{CheckoutSuccessMainSection.success}}" time="30" stepKey="waitForLoadSuccessPage1"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml index 05b85a3a55cb1..4741898b0ab86 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminTaxReportGridTest.xml @@ -123,9 +123,10 @@ <!-- Select shipping --> <actionGroup ref="orderSelectFlatRateShipping" stepKey="selectFlatRateShipping" after="fillCustomerAddress"/> - + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="SelectCheckMoneyPaymentMethod" after="selectFlatRateShipping" stepKey="selectCheckMoneyPayment"/> <!--Submit Order and verify information--> - <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="clickSubmitOrder" after="selectFlatRateShipping"/> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" after="selectCheckMoneyPayment" stepKey="clickSubmitOrder"/> <seeInCurrentUrl url="{{AdminOrderDetailsPage.url}}" stepKey="seeViewOrderPage" after="clickSubmitOrder"/> <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You created the order." stepKey="seeSuccessMessage" after="seeViewOrderPage"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml index 000fba1d88697..b0cdc2a3b224b 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest.xml @@ -92,9 +92,10 @@ <see stepKey="seeTotalExcl2" selector="{{CheckoutPaymentSection.orderSummaryTotalExcluding}}" userInput="$$virtualProduct1.price$$"/> <!-- Change the address --> - <actionGroup ref="GuestCheckoutFillNewBillingAddressActionGroup" stepKey="changeAddress"> + <actionGroup ref="GuestCheckoutSelectPaymentAndFillNewBillingAddressActionGroup" stepKey="changeAddress"> <argument name="customerVar" value="Simple_US_Customer_NY"/> <argument name="customerAddressVar" value="US_Address_NY"/> + <argument name="paymentMethod" value="Check / Money order"/> </actionGroup> <click stepKey="saveAddress" selector="{{CheckoutShippingSection.updateAddress}}"/> @@ -312,6 +313,8 @@ <argument name="Address" value="US_Address_CA"/> </actionGroup> <click stepKey="clickNext" selector="{{CheckoutShippingSection.next}}"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> <see stepKey="seeAddress" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxCA.state}}"/> <see stepKey="seeShipTo" selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{SimpleTaxCA.state}}"/> @@ -329,6 +332,8 @@ <argument name="Address" value="US_Address_NY"/> </actionGroup> <click stepKey="clickNext2" selector="{{CheckoutShippingSection.next}}"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> <see stepKey="seeShipTo2" selector="{{CheckoutPaymentSection.shipToInformation}}" userInput="{{SimpleTaxNY.state}}"/> <!-- Assert that taxes are applied correctly for NY --> @@ -424,6 +429,8 @@ <!-- Assert that taxes are applied correctly for NY --> <amOnPage url="{{CheckoutPage.url}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForShippingSection"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> <see stepKey="seeAddress" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxNY.state}}"/> <waitForElementVisible stepKey="waitForOverviewVisible" selector="{{CheckoutPaymentSection.tax}}"/> @@ -445,6 +452,8 @@ <click stepKey="saveAddress" selector="{{CheckoutShippingSection.updateAddress}}"/> <waitForPageLoad stepKey="waitForAddressSaved"/> + <!-- Checkout select Check/Money Order payment --> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/> <see stepKey="seeAddress2" selector="{{CheckoutShippingSection.defaultShipping}}" userInput="{{SimpleTaxCA.state}}"/> <!-- Assert that taxes are applied correctly for CA --> From 252b19901b6eef8b9f339b3a8581972bb1114258 Mon Sep 17 00:00:00 2001 From: Danny Verkade <danny@cream.nl> Date: Thu, 15 Nov 2018 11:20:45 +0100 Subject: [PATCH 147/704] - Removed hard coded precision in PriceCurrency class. - PriceCurrency now uses the DEFAULT_PRECISION constant for rounding. - Cleanup convertAndRound function, so rounding only takes place in one function. --- app/code/Magento/Directory/Model/PriceCurrency.php | 13 ++++--------- .../Framework/Pricing/PriceCurrencyInterface.php | 3 ++- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Directory/Model/PriceCurrency.php b/app/code/Magento/Directory/Model/PriceCurrency.php index a211242d377f3..0d8c7a40f8249 100644 --- a/app/code/Magento/Directory/Model/PriceCurrency.php +++ b/app/code/Magento/Directory/Model/PriceCurrency.php @@ -62,9 +62,7 @@ public function convert($amount, $scope = null, $currency = null) */ public function convertAndRound($amount, $scope = null, $currency = null, $precision = self::DEFAULT_PRECISION) { - $currentCurrency = $this->getCurrency($scope, $currency); - $convertedValue = $this->getStore($scope)->getBaseCurrency()->convert($amount, $currentCurrency); - return round($convertedValue, $precision); + return $this->round($this->convert($amount, $scope, $currency), $precision); } /** @@ -148,13 +146,10 @@ protected function getStore($scope = null) } /** - * Round price - * - * @param float $price - * @return float + * {@inheritdoc} */ - public function round($price) + public function round($price, $precision = self::DEFAULT_PRECISION) { - return round($price, 2); + return round($price, $precision); } } diff --git a/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php b/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php index 31f00d8b1345a..f3bad28b5011a 100644 --- a/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php +++ b/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php @@ -79,9 +79,10 @@ public function convertAndFormat( * Round price * * @param float $price + * @param int $precision * @return float */ - public function round($price); + public function round($price, $precision = self::DEFAULT_PRECISION); /** * Get currency model From 4ad77022371d7572dfb8944f46e5c1dab11722e2 Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Thu, 15 Nov 2018 02:52:34 -0800 Subject: [PATCH 148/704] MAGETWO-96222: Apply Category Permissions to Website 2 --- .../Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml | 2 +- app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml | 1 - app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml | 5 ++++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml b/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml index 9349e188430f4..f7d8abf8b2fea 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Page/AdminCategoryPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="AdminCategoryPage" url="catalog/category/" area="admin" module="Catalog"> + <page name="AdminCategoryPage" url="catalog/category/" area="admin" module="Magento_Catalog"> <section name="AdminCategorySidebarActionSection"/> <section name="AdminCategoryMainActionsSection"/> <section name="AdminCategorySidebarTreeSection"/> diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml index ce8336e23fc01..7b31623f237e9 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreGroupData.xml @@ -31,5 +31,4 @@ <data key="name">NewStore</data> <data key="code">Base1</data> </entity> - <entity name="customStoreGroup2" extends="customStoreGroup" type="group"/> </entities> diff --git a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml index 70b018907ee99..c1a8a552c3a97 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml @@ -19,5 +19,8 @@ <data key="store_type">website</data> <data key="website_id">null</data> </entity> - <entity name="customWebsite2" extends="customWebsite" type="website"/> + <entity name="secondCustomWebsite" extends="customWebsite" type="website"> + <data key="name" unique="suffix">Second Custom Website</data> + <data key="code" unique="suffix">second_custom_website</data> + </entity> </entities> From 401cad4c0df4bd9112d9537ccc379e243bf2dd7b Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 15 Nov 2018 14:15:41 +0200 Subject: [PATCH 149/704] MAGETWO-96016: The value Quantity of Advenced Pricing isn't saved correctly --- .../Backend/TierPrice/AbstractHandler.php | 101 ++++++++++++++++++ .../Backend/TierPrice/SaveHandler.php | 65 +---------- .../Backend/TierPrice/UpdateHandler.php | 71 ++---------- .../Attribute/Backend/TierpriceTest.php | 69 ++++++++---- .../Magento/Catalog/_files/product_simple.php | 10 ++ 5 files changed, 172 insertions(+), 144 deletions(-) create mode 100644 app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/AbstractHandler.php diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/AbstractHandler.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/AbstractHandler.php new file mode 100644 index 0000000000000..65e36633a68c5 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/AbstractHandler.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Model\Product\Attribute\Backend\TierPrice; + +use Magento\Framework\EntityManager\Operation\ExtensionInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Customer\Api\GroupManagementInterface; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice; + +/** + * Tier price data abstract handler. + */ +abstract class AbstractHandler implements ExtensionInterface +{ + /** + * @var \Magento\Customer\Api\GroupManagementInterface + */ + protected $groupManagement; + + /** + * @param \Magento\Customer\Api\GroupManagementInterface $groupManagement + */ + public function __construct( + GroupManagementInterface $groupManagement + ) { + $this->groupManagement = $groupManagement; + } + + /** + * Get additional tier price fields. + * + * @param array $objectArray + * @return array + */ + protected function getAdditionalFields(array $objectArray): array + { + $percentageValue = $this->getPercentage($objectArray); + + return [ + 'value' => $percentageValue ? null : $objectArray['price'], + 'percentage_value' => $percentageValue ?: null, + ]; + } + + /** + * Check whether price has percentage value. + * + * @param array $priceRow + * @return int|null + */ + protected function getPercentage(array $priceRow): ?int + { + return isset($priceRow['percentage_value']) && is_numeric($priceRow['percentage_value']) + ? (int)$priceRow['percentage_value'] + : null; + } + + /** + * Prepare tier price data by provided price row data. + * + * @param array $data + * @return array + * @throws \Magento\Framework\Exception\LocalizedException + */ + protected function prepareTierPrice(array $data): array + { + $useForAllGroups = (int)$data['cust_group'] === $this->groupManagement->getAllCustomersGroup()->getId(); + $customerGroupId = $useForAllGroups ? 0 : $data['cust_group']; + $tierPrice = array_merge( + $this->getAdditionalFields($data), + [ + 'website_id' => $data['website_id'], + 'all_groups' => (int)$useForAllGroups, + 'customer_group_id' => $customerGroupId, + 'value' => $data['price'] ?? null, + 'qty' => $this->parseQty($data['price_qty']), + ] + ); + + return $tierPrice; + } + + /** + * Parse quantity value into float. + * + * @param mixed $value + * @return float|int + */ + protected function parseQty($value) + { + return $value * 1; + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/SaveHandler.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/SaveHandler.php index 248d8ed221250..9cb2ac0145898 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/SaveHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/SaveHandler.php @@ -18,7 +18,7 @@ /** * Process tier price data for handled new product */ -class SaveHandler implements ExtensionInterface +class SaveHandler extends AbstractHandler { /** * @var \Magento\Store\Model\StoreManagerInterface @@ -30,11 +30,6 @@ class SaveHandler implements ExtensionInterface */ private $attributeRepository; - /** - * @var \Magento\Customer\Api\GroupManagementInterface - */ - private $groupManagement; - /** * @var \Magento\Framework\EntityManager\MetadataPool */ @@ -59,9 +54,10 @@ public function __construct( MetadataPool $metadataPool, Tierprice $tierPriceResource ) { + parent::__construct($groupManagement); + $this->storeManager = $storeManager; $this->attributeRepository = $attributeRepository; - $this->groupManagement = $groupManagement; $this->metadataPoll = $metadataPool; $this->tierPriceResource = $tierPriceResource; } @@ -72,8 +68,6 @@ public function __construct( * @param \Magento\Catalog\Api\Data\ProductInterface|object $entity * @param array $arguments * @return \Magento\Catalog\Api\Data\ProductInterface|object - * @throws \Magento\Framework\Exception\NoSuchEntityException - * @throws \Magento\Framework\Exception\LocalizedException * @throws \Magento\Framework\Exception\InputException * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -115,57 +109,4 @@ public function execute($entity, $arguments = []) return $entity; } - - /** - * Get additional tier price fields - * - * @param array $objectArray - * @return array - */ - private function getAdditionalFields(array $objectArray): array - { - $percentageValue = $this->getPercentage($objectArray); - return [ - 'value' => $percentageValue ? null : $objectArray['price'], - 'percentage_value' => $percentageValue ?: null, - ]; - } - - /** - * Check whether price has percentage value. - * - * @param array $priceRow - * @return int|null - */ - private function getPercentage(array $priceRow): ?int - { - return isset($priceRow['percentage_value']) && is_numeric($priceRow['percentage_value']) - ? (int)$priceRow['percentage_value'] - : null; - } - - /** - * Prepare tier price data by provided price row data - * - * @param array $data - * @return array - * @throws \Magento\Framework\Exception\LocalizedException - */ - private function prepareTierPrice(array $data): array - { - $useForAllGroups = (int)$data['cust_group'] === $this->groupManagement->getAllCustomersGroup()->getId(); - $customerGroupId = $useForAllGroups ? 0 : $data['cust_group']; - $tierPrice = array_merge( - $this->getAdditionalFields($data), - [ - 'website_id' => $data['website_id'], - 'all_groups' => (int)$useForAllGroups, - 'customer_group_id' => $customerGroupId, - 'value' => $data['price'] ?? null, - 'qty' => (int)$data['price_qty'] - ] - ); - - return $tierPrice; - } } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php index b4d6dc2c19e51..6ac7825c991f8 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php @@ -16,9 +16,9 @@ use Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice; /** - * Process tier price data for handled existing product + * Process tier price data for handled existing product. */ -class UpdateHandler implements ExtensionInterface +class UpdateHandler extends AbstractHandler { /** * @var \Magento\Store\Model\StoreManagerInterface @@ -30,11 +30,6 @@ class UpdateHandler implements ExtensionInterface */ private $attributeRepository; - /** - * @var \Magento\Customer\Api\GroupManagementInterface - */ - private $groupManagement; - /** * @var \Magento\Framework\EntityManager\MetadataPool */ @@ -59,9 +54,10 @@ public function __construct( MetadataPool $metadataPool, Tierprice $tierPriceResource ) { + parent::__construct($groupManagement); + $this->storeManager = $storeManager; $this->attributeRepository = $attributeRepository; - $this->groupManagement = $groupManagement; $this->metadataPoll = $metadataPool; $this->tierPriceResource = $tierPriceResource; } @@ -72,8 +68,7 @@ public function __construct( * @param \Magento\Catalog\Api\Data\ProductInterface|object $entity * @param array $arguments * @return \Magento\Catalog\Api\Data\ProductInterface|object - * @throws \Magento\Framework\Exception\NoSuchEntityException - * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\InputException * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function execute($entity, $arguments = []) @@ -119,34 +114,6 @@ public function execute($entity, $arguments = []) return $entity; } - /** - * Get additional tier price fields - * - * @param array $objectArray - * @return array - */ - private function getAdditionalFields(array $objectArray): array - { - $percentageValue = $this->getPercentage($objectArray); - return [ - 'value' => $percentageValue ? null : $objectArray['price'], - 'percentage_value' => $percentageValue ?: null, - ]; - } - - /** - * Check whether price has percentage value. - * - * @param array $priceRow - * @return int|null - */ - private function getPercentage(array $priceRow): ?int - { - return isset($priceRow['percentage_value']) && is_numeric($priceRow['percentage_value']) - ? (int)$priceRow['percentage_value'] - : null; - } - /** * Update existing tier prices for processed product * @@ -226,39 +193,15 @@ private function deleteValues(int $productId, array $valuesToDelete): bool */ private function getPriceKey(array $priceData): string { + $qty = $this->parseQty($priceData['price_qty']); $key = implode( '-', - array_merge([$priceData['website_id'], $priceData['cust_group']], [(int)$priceData['price_qty']]) + array_merge([$priceData['website_id'], $priceData['cust_group']], [$qty]) ); return $key; } - /** - * Prepare tier price data by provided price row data - * - * @param array $data - * @return array - * @throws \Magento\Framework\Exception\LocalizedException - */ - private function prepareTierPrice(array $data): array - { - $useForAllGroups = (int)$data['cust_group'] === $this->groupManagement->getAllCustomersGroup()->getId(); - $customerGroupId = $useForAllGroups ? 0 : $data['cust_group']; - $tierPrice = array_merge( - $this->getAdditionalFields($data), - [ - 'website_id' => $data['website_id'], - 'all_groups' => (int)$useForAllGroups, - 'customer_group_id' => $customerGroupId, - 'value' => $data['price'] ?? null, - 'qty' => (int)$data['price_qty'] - ] - ); - - return $tierPrice; - } - /** * Check by id is website global * diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/TierpriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/TierpriceTest.php index 0ab74788bfd3b..973d290d61466 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/TierpriceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/TierpriceTest.php @@ -68,27 +68,49 @@ public function testValidate() [ ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 2, 'price' => 8], ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 5, 'price' => 5], + ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 5.6, 'price' => 4], ] ); $this->assertTrue($this->_model->validate($product)); } /** + * Test that duplicated tier price values issues exception during validation. + * + * @dataProvider validateDuplicateDataProvider * @expectedException \Magento\Framework\Exception\LocalizedException */ - public function testValidateDuplicate() + public function testValidateDuplicate(array $tierPricesData) { $product = new \Magento\Framework\DataObject(); - $product->setTierPrice( - [ - ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 2, 'price' => 8], - ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 2, 'price' => 8], - ] - ); + $product->setTierPrice($tierPricesData); $this->_model->validate($product); } + /** + * testValidateDuplicate data provider. + * + * @return array + */ + public function validateDuplicateDataProvider(): array + { + return [ + [ + [ + ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 2, 'price' => 8], + ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 2, 'price' => 8], + ], + ], + [ + [ + ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 2.2, 'price' => 8], + ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 2.2, 'price' => 8], + ], + ], + ]; + } + /** * @expectedException \Magento\Framework\Exception\LocalizedException */ @@ -97,9 +119,9 @@ public function testValidateDuplicateWebsite() $product = new \Magento\Framework\DataObject(); $product->setTierPrice( [ - ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 2, 'price' => 8], - ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 5, 'price' => 5], - ['website_id' => 1, 'cust_group' => 1, 'price_qty' => 5, 'price' => 5], + ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 2.2, 'price' => 8], + ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 5.3, 'price' => 5], + ['website_id' => 1, 'cust_group' => 1, 'price_qty' => 5.3, 'price' => 5], ] ); @@ -127,12 +149,17 @@ public function testPreparePriceData() ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 2, 'price' => 8], ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 5, 'price' => 5], ['website_id' => 1, 'cust_group' => 1, 'price_qty' => 5, 'price' => 5], + ['website_id' => 1, 'cust_group' => 1, 'price_qty' => 5.3, 'price' => 4], + ['website_id' => 1, 'cust_group' => 1, 'price_qty' => 5.4, 'price' => 3], + ['website_id' => 1, 'cust_group' => 1, 'price_qty' => '5.40', 'price' => 2], ]; $newData = $this->_model->preparePriceData($data, \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE, 1); - $this->assertEquals(2, count($newData)); + $this->assertEquals(4, count($newData)); $this->assertArrayHasKey('1-2', $newData); $this->assertArrayHasKey('1-5', $newData); + $this->assertArrayHasKey('1-5.3', $newData); + $this->assertArrayHasKey('1-5.4', $newData); } public function testAfterLoad() @@ -148,7 +175,7 @@ public function testAfterLoad() $this->_model->afterLoad($product); $price = $product->getTierPrice(); $this->assertNotEmpty($price); - $this->assertEquals(4, count($price)); + $this->assertEquals(5, count($price)); } /** @@ -187,6 +214,7 @@ public function saveExistingProductDataProvider(): array ['website_id' => 0, 'customer_group_id' => 32000, 'qty' => 2, 'value' => 8], ['website_id' => 0, 'customer_group_id' => 32000, 'qty' => 5, 'value' => 5], ['website_id' => 0, 'customer_group_id' => 0, 'qty' => 3, 'value' => 5], + ['website_id' => 0, 'customer_group_id' => 0, 'qty' => 3.2, 'value' => 6], [ 'website_id' => 0, 'customer_group_id' => 0, @@ -194,13 +222,14 @@ public function saveExistingProductDataProvider(): array 'extension_attributes' => new \Magento\Framework\DataObject(['percentage_value' => 50]) ], ], - 4, + 5, ], 'update one' => [ [ ['website_id' => 0, 'customer_group_id' => 32000, 'qty' => 2, 'value' => 8], ['website_id' => 0, 'customer_group_id' => 32000, 'qty' => 5, 'value' => 5], ['website_id' => 0, 'customer_group_id' => 0, 'qty' => 3, 'value' => 5], + ['website_id' => 0, 'customer_group_id' => 0, 'qty' => '3.2', 'value' => 6], [ 'website_id' => 0, 'customer_group_id' => 0, @@ -208,12 +237,13 @@ public function saveExistingProductDataProvider(): array 'extension_attributes' => new \Magento\Framework\DataObject(['percentage_value' => 10]) ], ], - 4, + 5, ], 'delete one' => [ [ ['website_id' => 0, 'customer_group_id' => 32000, 'qty' => 5, 'value' => 5], ['website_id' => 0, 'customer_group_id' => 0, 'qty' => 3, 'value' => 5], + ['website_id' => 0, 'customer_group_id' => 0, 'qty' => '3.2', 'value' => 6], [ 'website_id' => 0, 'customer_group_id' => 0, @@ -221,13 +251,14 @@ public function saveExistingProductDataProvider(): array 'extension_attributes' => new \Magento\Framework\DataObject(['percentage_value' => 50]) ], ], - 3, + 4, ], 'add one' => [ [ ['website_id' => 0, 'customer_group_id' => 32000, 'qty' => 2, 'value' => 8], ['website_id' => 0, 'customer_group_id' => 32000, 'qty' => 5, 'value' => 5], ['website_id' => 0, 'customer_group_id' => 0, 'qty' => 3, 'value' => 5], + ['website_id' => 0, 'customer_group_id' => 0, 'qty' => 3.2, 'value' => 6], [ 'website_id' => 0, 'customer_group_id' => 32000, @@ -241,7 +272,7 @@ public function saveExistingProductDataProvider(): array 'extension_attributes' => new \Magento\Framework\DataObject(['percentage_value' => 50]) ], ], - 5, + 6, ], 'delete all' => [[], 0,], ]; @@ -270,7 +301,7 @@ public function testSaveNewProduct(array $tierPricesData, int $tierPriceCount): $tierPrices = []; foreach ($tierPricesData as $tierPrice) { $tierPrices[] = $this->tierPriceFactory->create([ - 'data' => $tierPrice + 'data' => $tierPrice, ]); } $product->setTierPrices($tierPrices); @@ -290,6 +321,8 @@ public function saveNewProductDataProvider(): array ['website_id' => 0, 'customer_group_id' => 32000, 'qty' => 2, 'value' => 8], ['website_id' => 0, 'customer_group_id' => 32000, 'qty' => 5, 'value' => 5], ['website_id' => 0, 'customer_group_id' => 0, 'qty' => 3, 'value' => 5], + ['website_id' => 0, 'customer_group_id' => 0, 'qty' => '3.2', 'value' => 4], + ['website_id' => 0, 'customer_group_id' => 0, 'qty' => '3.3', 'value' => 3], [ 'website_id' => 0, 'customer_group_id' => 0, @@ -297,7 +330,7 @@ public function saveNewProductDataProvider(): array 'extension_attributes' => new \Magento\Framework\DataObject(['percentage_value' => 50]) ], ], - 4, + 6, ], ]; } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php index 82fe2e1f30283..6e0784a8a33ff 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php @@ -60,6 +60,16 @@ ] )->setExtensionAttributes($tierPriceExtensionAttributes1); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => \Magento\Customer\Model\Group::NOT_LOGGED_IN_ID, + 'qty' => 3.2, + 'value' => 6 + ] + ] +)->setExtensionAttributes($tierPriceExtensionAttributes1); + $tierPriceExtensionAttributes2 = $tpExtensionAttributesFactory->create() ->setWebsiteId($adminWebsite->getId()) ->setPercentageValue(50); From a3dbeaec7a772f0337f5c606123d765c19db2b9d Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Thu, 15 Nov 2018 15:31:15 +0200 Subject: [PATCH 150/704] MAGETWO-96016: The value Quantity of Advenced Pricing isn't saved correctly --- .../Magento/Catalog/Api/ProductTierPriceManagementTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductTierPriceManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductTierPriceManagementTest.php index 9562d6618d8db..d5c035733942e 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductTierPriceManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductTierPriceManagementTest.php @@ -49,7 +49,7 @@ public function testGetList($customerGroupId, $count, $value, $qty) public function getListDataProvider() { return [ - [0, 2, 5, 3], + [0, 3, 5, 3], [1, 0, null, null], ['all', 2, 8, 2], ]; From 715ae96da3204d41be0590c9dcada03b8f8892f3 Mon Sep 17 00:00:00 2001 From: DmytroPaidych <dimonovp@gmail.com> Date: Thu, 15 Nov 2018 06:10:19 -0800 Subject: [PATCH 151/704] MAGETWO-96222: Apply Category Permissions to Website 2 --- app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml index c1a8a552c3a97..f636336524f01 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/WebsiteData.xml @@ -19,8 +19,8 @@ <data key="store_type">website</data> <data key="website_id">null</data> </entity> - <entity name="secondCustomWebsite" extends="customWebsite" type="website"> - <data key="name" unique="suffix">Second Custom Website</data> - <data key="code" unique="suffix">second_custom_website</data> + <entity name="secondCustomWebsite" extends="customWebsite"> + <data key="name" unique="suffix">Custom Website</data> + <data key="code" unique="suffix">custom_website</data> </entity> </entities> From d15cca6755976100b4559351594bd5fa8f22d832 Mon Sep 17 00:00:00 2001 From: Leonid Poluyanov <lpoluyanov@magento.com> Date: Thu, 15 Nov 2018 15:32:22 +0200 Subject: [PATCH 152/704] MAGETWO-96312: Delete Address button on customer address modal does not close and returns json --- .../Adminhtml/Edit/Address/DeleteButton.php | 3 +- .../Controller/Adminhtml/Address/Delete.php | 1 - .../Edit/Address/DeleteButtonTest.php | 106 ++++++++++++++++++ .../ui_component/customer_address_form.xml | 2 +- .../adminhtml/web/js/form/components/form.js | 11 +- .../web/js/form/components/insert-form.js | 6 + .../js/view/form/components/form.test.js | 71 ++++++++++++ 7 files changed, 194 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Address/DeleteButtonTest.php create mode 100644 dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/form.test.js diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php index 7296a5167622a..da589a25df28e 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Address/DeleteButton.php @@ -23,7 +23,6 @@ class DeleteButton extends GenericButton implements ButtonProviderInterface public function getButtonData() { $data = []; - $confirm = __('Are you sure you want to delete this address?'); if ($this->getAddressId()) { $data = [ 'label' => __('Delete'), @@ -56,7 +55,7 @@ public function getButtonData() * @return string * @throws \Magento\Framework\Exception\LocalizedException */ - public function getDeleteUrl(): string + private function getDeleteUrl(): string { return $this->getUrl( Actions::CUSTOMER_ADDRESS_PATH_DELETE, diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php index d14cf86e4084e..711cd2473cdc5 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Address/Delete.php @@ -8,7 +8,6 @@ namespace Magento\Customer\Controller\Adminhtml\Address; use Magento\Backend\App\Action; -use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\Controller\Result\Json; use Magento\Framework\Controller\Result\JsonFactory; diff --git a/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Address/DeleteButtonTest.php b/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Address/DeleteButtonTest.php new file mode 100644 index 0000000000000..7b0da3bd422a6 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/Block/Adminhtml/Edit/Address/DeleteButtonTest.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Test\Unit\Block\Adminhtml\Edit\Address; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Class for \Magento\Customer\Block\Adminhtml\Edit\Address\DeleteButton unit tests + */ +class DeleteButtonTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Customer\Model\AddressFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $addressFactory; + + /** + * @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $urlBuilder; + + /** + * @var \Magento\Customer\Model\ResourceModel\Address|\PHPUnit_Framework_MockObject_MockObject + */ + private $addressResourceModel; + + /** + * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $request; + + /** + * @var \Magento\Customer\Model\ResourceModel\AddressRepository|\PHPUnit_Framework_MockObject_MockObject + */ + private $addressRepository; + + /** + * @var \Magento\Customer\Block\Adminhtml\Edit\Address\DeleteButton + */ + private $deleteButton; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->addressFactory = $this->getMockBuilder(\Magento\Customer\Model\AddressFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->urlBuilder = $this->getMockForAbstractClass(\Magento\Framework\UrlInterface::class); + $this->addressResourceModel = $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Address::class) + ->disableOriginalConstructor() + ->getMock(); + $this->request = $this->getMockForAbstractClass(\Magento\Framework\App\RequestInterface::class); + $this->addressRepository = $this->getMockBuilder( + \Magento\Customer\Model\ResourceModel\AddressRepository::class + ) + ->disableOriginalConstructor() + ->getMock(); + $objectManagerHelper = new ObjectManagerHelper($this); + + $this->deleteButton = $objectManagerHelper->getObject( + \Magento\Customer\Block\Adminhtml\Edit\Address\DeleteButton::class, + [ + 'addressFactory' => $this->addressFactory, + 'urlBuilder' => $this->urlBuilder, + 'addressResourceModel' => $this->addressResourceModel, + 'request' => $this->request, + 'addressRepository' => $this->addressRepository, + ] + ); + } + + /** + * Unit test for \Magento\Customer\Block\Adminhtml\Edit\Address\DeleteButton::getButtonData() method + */ + public function testGetButtonData() + { + $addressId = 1; + $customerId = 2; + + /** @var \Magento\Customer\Model\Address|\PHPUnit_Framework_MockObject_MockObject $address */ + $address = $this->getMockBuilder(\Magento\Customer\Model\Address::class) + ->disableOriginalConstructor() + ->getMock(); + $address->expects($this->atLeastOnce())->method('getEntityId')->willReturn($addressId); + $address->expects($this->atLeastOnce())->method('getCustomerId')->willReturn($customerId); + $this->addressFactory->expects($this->atLeastOnce())->method('create')->willReturn($address); + $this->request->expects($this->atLeastOnce())->method('getParam')->with('entity_id') + ->willReturn($addressId); + $this->addressResourceModel->expects($this->atLeastOnce())->method('load')->with($address, $addressId); + $this->addressRepository->expects($this->atLeastOnce())->method('getById')->with($addressId) + ->willReturn($address); + $this->urlBuilder->expects($this->atLeastOnce())->method('getUrl') + ->with( + \Magento\Customer\Ui\Component\Listing\Address\Column\Actions::CUSTOMER_ADDRESS_PATH_DELETE, + ['parent_id' => $customerId, 'id' => $addressId] + )->willReturn('url'); + + $buttonData = $this->deleteButton->getButtonData(); + $this->assertEquals('Delete', (string)$buttonData['label']); + } +} diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index afe2396a9062f..fdef0e959443b 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -11,7 +11,7 @@ <item name="provider" xsi:type="string">customer_address_form.customer_address_form_data_source</item> </item> <item name="config" xsi:type="array"> - <item name="confirmationMessage" translate="true" xsi:type="string">Are you sure you want to delete this address?</item> + <item name="deleteConfirmationMessage" translate="true" xsi:type="string">Are you sure you want to delete this address?</item> </item> <item name="label" xsi:type="string" translate="true">Update Address</item> <item name="reverseMetadataMerge" xsi:type="boolean">true</item> diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js index 34cbbfefce368..e1c5f01c731a0 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/form.js @@ -13,18 +13,25 @@ define([ return Form.extend({ defaults: { - confirmationMessage: '', + deleteConfirmationMessage: '', ajaxSettings: { method: 'POST', dataType: 'json' } }, + /** + * Delete customer address by provided url. + * Will call confirmation message to be sure that user is really wants to delete this address + * + * @param {String} url - ajax url + */ deleteAddress: function (url) { + console.log(url); var that = this; uiConfirm({ - content: this.confirmationMessage, + content: this.deleteConfirmationMessage, actions: { /** @inheritdoc */ confirm: function () { diff --git a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js index 59c3e8e4b8b0b..45d0ff112f096 100644 --- a/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js +++ b/app/code/Magento/Customer/view/adminhtml/web/js/form/components/insert-form.js @@ -52,6 +52,12 @@ define([ } }, + /** + * Event method that closes "Edit customer address" modal and refreshes grid after customer address + * was removed through "Delete" button on the "Edit customer address" modal + * + * @param {string} id - customer address ID to delete + */ onAddressDelete: function (id) { this.addressModal().closeModal(); this.addressListing().reload({ diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/form.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/form.test.js new file mode 100644 index 0000000000000..99ce913e87390 --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/form.test.js @@ -0,0 +1,71 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'underscore', + 'uiRegistry', + 'Magento_Customer/js/form/components/form', + 'jquery' +], function (_, registry, Constr, $) { + 'use strict'; + + describe('Magento_Customer/js/form/components/form', function () { + + var obj, + originaljQueryAjax; + + beforeEach (function () { + originaljQueryAjax = $.ajax; + obj = new Constr({ + provider: 'provName', + name: '', + index: '' + }); + }); + + afterEach(function () { + $.ajax = originaljQueryAjax; + }); + + registry.set('provName', { + /** Stub */ + on: function () {}, + + /** Stub */ + get: function () {}, + + /** Stub */ + set: function () {} + }); + + describe('"deleteAddress" method', function () { + it('Check for defined ', function () { + expect(obj.hasOwnProperty('deleteAddress')).toBeDefined(); + }); + it('Check method type', function () { + var type = typeof obj.deleteAddress; + + expect(type).toEqual('function'); + }); + it('Check returned value if method called without arguments', function () { + expect(obj.deleteAddress()).toBeUndefined(); + }); + it('Check returned value type if method called without arguments', function () { + var type = typeof obj.deleteAddress(); + + expect(type).toEqual('undefined'); + }); + it('Should call not call ajax if arguments are empty', function () { + $.ajax = jasmine.createSpy(); + + spyOn(obj, 'deleteAddress'); + + expect(obj.deleteAddress()).toBeUndefined(); + + expect($.ajax).not.toHaveBeenCalled(); + }); + }); + }); +}); From 676c9bf26327cadf2e2ed669ee868104f41d526a Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 16 Nov 2018 11:14:14 +0200 Subject: [PATCH 153/704] MAGETWO-96371: Category tree display wrong number of product --- .../ResourceModel/Category/Collection.php | 4 +- ...CategoryProductsUsingScopeSelectorTest.xml | 50 +++++++++++-------- .../AdminDeleteWebsiteActionGroup.xml | 3 +- .../Mftf/Section/AdminStoresGridSection.xml | 2 +- 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php index 46bb74513b59c..618abda0a942d 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php @@ -323,9 +323,7 @@ public function loadProductCount($items, $countRegular = true, $countAnchor = tr 'main_table.category_id=e.entity_id', [] )->where( - 'e.entity_id = :entity_id' - )->orWhere( - 'e.path LIKE :c_path' + '(e.entity_id = :entity_id OR e.path LIKE :c_path)' ); if ($websiteId) { $select->join( diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml index a748635ac9a53..b6ae1d31f4688 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminFilteringCategoryProductsUsingScopeSelectorTest.xml @@ -50,14 +50,12 @@ </createData> <!-- Set filter to product name and product0 not assigned to any website--> - <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="filterGroupedProductOptions"> - <argument name="product" value="_defaultProduct"/> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForProduct0"> + <argument name="product" value="$$createProduct0$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="clickOpenProductForEdit0"> + <argument name="product" value="$$createProduct0$$"/> </actionGroup> - - <click selector="{{AdminProductGridSection.productGridNameProduct('$$createProduct0.name$$')}}" - stepKey="clickOpenProductForEdit"/> - <waitForPageLoad time="30" stepKey="waitForProductEditOpen"/> - <scrollTo selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="scrollToWebsitesSection"/> <click selector="{{ProductInWebsitesSection.sectionHeader}}" stepKey="clickToOpenWebsiteSection"/> <waitForPageLoad stepKey="waitForToOpenedWebsiteSection"/> @@ -67,12 +65,12 @@ stepKey="seeSuccessMessage"/> <!-- Set filter to product name and product2 in website 2 only --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> - <waitForPageLoad time="30" stepKey="waitForProductsPageToLoad"/> - <click selector="{{AdminProductGridSection.productGridNameProduct('$$createProduct2.name$$')}}" - stepKey="clickOpenProductForEdit1"/> - <waitForPageLoad time="30" stepKey="waitForProductEditOpen1"/> - + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForProduct2"> + <argument name="product" value="$$createProduct2$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="clickOpenProductForEdit2"> + <argument name="product" value="$$createProduct2$$"/> + </actionGroup> <actionGroup ref="SelectProductInWebsitesActionGroup" stepKey="selectProductInWebsites"> <argument name="website" value="secondWebsite"/> </actionGroup> @@ -82,12 +80,12 @@ stepKey="seeSuccessMessage1"/> <!-- Set filter to product name and product12 assigned to both websites 1 and 2 --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex1"/> - <waitForPageLoad time="30" stepKey="waitForProductsPageToLoad1"/> - <click selector="{{AdminProductGridSection.productGridNameProduct('$$createProduct12.name$$')}}" - stepKey="clickOpenProductForEdit2"/> - <waitForPageLoad time="30" stepKey="waitForProductEditOpen2"/> - + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForProduct12"> + <argument name="product" value="$$createProduct12$$"/> + </actionGroup> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="clickOpenProductForEdit12"> + <argument name="product" value="$$createProduct12$$"/> + </actionGroup> <actionGroup ref="SelectProductInWebsitesActionGroup" stepKey="selectProductInWebsites1"> <argument name="website" value="secondWebsite"/> </actionGroup> @@ -113,6 +111,10 @@ <click selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createCategory.name$$)}}" stepKey="clickCategoryName"/> <click selector="{{AdminCategoryProductsSection.sectionHeader}}" stepKey="openProductSection"/> + <grabTextFrom selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createCategory.name$$)}}" + stepKey="grabTextFromCategory"/> + <assertRegExp expected="/\(4\)$/" expectedType="string" actual="$grabTextFromCategory" actualType="variable" + message="wrongCountProductOnAllStoreViews" stepKey="checkCountProducts"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct0.name$$)}}" userInput="$$createProduct0.name$$" stepKey="seeProductName"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct1.name$$)}}" @@ -135,6 +137,10 @@ <waitForElementNotVisible selector="{{AdminCategoryMainActionsSection.CategoryStoreViewModalAccept}}" stepKey="waitForNotVisibleModalAccept"/> <waitForPageLoad stepKey="waitForCategoryPageLoad2"/> + <grabTextFrom selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createCategory.name$$)}}" + stepKey="grabTextFromCategory1"/> + <assertRegExp expected="/\(2\)$/" expectedType="string" actual="$grabTextFromCategory1" actualType="variable" + message="wrongCountProductOnWebsite1" stepKey="checkCountProducts1"/> <click selector="{{AdminCategoryProductsSection.sectionHeader}}" stepKey="openProductSection1"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct1.name$$)}}" userInput="$$createProduct1.name$$" stepKey="seeProductName4"/> @@ -145,7 +151,7 @@ <dontSee selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct2.name$$)}}" userInput="$$createProduct2.name$$" stepKey="dontSeeProductName1"/> - <!-- Step 4: Set scope selector to Website2 ( StopreView for Website 2) --> + <!-- Step 4: Set scope selector to Website2 ( StoreView for Website 2) --> <scrollToTopOfPage stepKey="scrollToTopOfPage1"/> <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewDropdownToggle}}" stepKey="clickStoresList1"/> @@ -160,6 +166,10 @@ stepKey="waitForNotVisibleModalAccept1"/> <waitForPageLoad stepKey="waitForCategoryPageLoad4"/> <click selector="{{AdminCategoryProductsSection.sectionHeader}}" stepKey="openProductSection2"/> + <grabTextFrom selector="{{AdminCategorySidebarTreeSection.categoryInTree($$createCategory.name$$)}}" + stepKey="grabTextFromCategory2"/> + <assertRegExp expected="/\(2\)$/" expectedType="string" actual="$grabTextFromCategory2" actualType="variable" + message="wrongCountProductOnWebsite2" stepKey="checkCountProducts2"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct2.name$$)}}" userInput="$$createProduct2.name$$" stepKey="seeProductName6"/> <see selector="{{AdminCategoryProductsGridSection.productGridNameProduct($$createProduct12.name$$)}}" diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml index 1400fbf12c16c..58fd0a3f0bc2b 100644 --- a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml +++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminDeleteWebsiteActionGroup.xml @@ -23,5 +23,6 @@ <click selector="{{AdminStoresDeleteStoreGroupSection.deleteStoreGroupButton}}" stepKey="clickDeleteWebsiteButton"/> <waitForElementVisible selector="{{AdminStoresGridSection.websiteFilterTextField}}" stepKey="waitForStoreGridToReload"/> <see userInput="You deleted the website." stepKey="seeSavedMessage"/> + <click selector="{{AdminStoresGridSection.resetButton}}" stepKey="resetSearchFilter2"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml index 04cbeb5bc596e..1b84027a5dd4a 100644 --- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml +++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection.xml @@ -16,7 +16,7 @@ <element name="websiteFilterTextField" type="input" selector="#storeGrid_filter_website_title"/> <element name="storeFilterTextField" type="input" selector="#storeGrid_filter_store_title"/> <element name="searchButton" type="button" selector=".admin__data-grid-header button[title=Search]" timeout="30"/> - <element name="resetButton" type="button" selector="button[title='Reset Filter']"/> + <element name="resetButton" type="button" selector="button[title='Reset Filter']" timeout="30"/> <element name="websiteNameInFirstRow" type="text" selector=".col-website_title>a"/> <element name="storeGrpNameInFirstRow" type="text" selector=".col-group_title>a"/> <element name="storeNameInFirstRow" type="text" selector=".col-store_title>a"/> From cf3433d2485ea00a212f4e9f250aa96c9ad95cd5 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 16 Nov 2018 11:41:48 +0200 Subject: [PATCH 154/704] MAGETWO-96389: Incorrect product displaying on storefront in IE11 --- .../Magento/blank/Magento_Theme/web/css/source/_module.less | 4 ---- .../Magento/luma/Magento_Theme/web/css/source/_module.less | 4 ---- 2 files changed, 8 deletions(-) diff --git a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less index 8df5556e97721..b314bcf5b3473 100644 --- a/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Theme/web/css/source/_module.less @@ -296,10 +296,6 @@ box-sizing: border-box; width: 100%; } - - .ie11 & { - height: 100%; - } } .navigation ul { diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less index bafe1be57ee35..cadf575b95fc7 100644 --- a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less @@ -627,10 +627,6 @@ box-sizing: border-box; width: 100%; } - - .ie11 & { - height: 100%; - } } .page-footer { From 51f14883ce9b5ec66d92f9675146a3f443bdae0b Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Fri, 16 Nov 2018 12:00:23 +0200 Subject: [PATCH 155/704] MAGETWO-96016: The value Quantity of Advenced Pricing isn't saved correctly --- .../testsuite/Magento/Catalog/_files/product_simple.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php index 6e0784a8a33ff..514c6563622c9 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php @@ -65,7 +65,7 @@ 'data' => [ 'customer_group_id' => \Magento\Customer\Model\Group::NOT_LOGGED_IN_ID, 'qty' => 3.2, - 'value' => 6 + 'value' => 6, ] ] )->setExtensionAttributes($tierPriceExtensionAttributes1); From ef2cf53a60cc7a60895685230c8ad52181de0c3e Mon Sep 17 00:00:00 2001 From: Dmytro Poperechnyy <dpoperechnyy@magento.com> Date: Fri, 16 Nov 2018 16:26:41 +0200 Subject: [PATCH 156/704] MAGETWO-96330: Default Shipping Address Edit button is displayed incorrectly --- .../ui_component/customer_address_form.xml | 4 +- .../web/template/default-address-wrapper.html | 7 + .../view/base/ui_component/customer_form.xml | 185 +++++++++--------- .../web/css/source/_module.less | 27 +-- 4 files changed, 111 insertions(+), 112 deletions(-) create mode 100644 app/code/Magento/Customer/view/adminhtml/web/template/default-address-wrapper.html diff --git a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml index fdef0e959443b..e07c5d97a30a6 100644 --- a/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml +++ b/app/code/Magento/Customer/view/adminhtml/ui_component/customer_address_form.xml @@ -80,7 +80,7 @@ <dataType>text</dataType> </settings> </field> - <field name="default_billing" sortOrder="6" formElement="checkbox"> + <field name="default_billing" sortOrder="5" formElement="checkbox"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="default" xsi:type="number">0</item> @@ -103,7 +103,7 @@ </checkbox> </formElements> </field> - <field name="default_shipping" sortOrder="5" formElement="checkbox"> + <field name="default_shipping" sortOrder="7" formElement="checkbox"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="default" xsi:type="number">0</item> diff --git a/app/code/Magento/Customer/view/adminhtml/web/template/default-address-wrapper.html b/app/code/Magento/Customer/view/adminhtml/web/template/default-address-wrapper.html new file mode 100644 index 0000000000000..2af366b03342c --- /dev/null +++ b/app/code/Magento/Customer/view/adminhtml/web/template/default-address-wrapper.html @@ -0,0 +1,7 @@ +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<div class="customer-default-address-wrapper" each="data: elems, as: 'element'" render=""/> diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index a7c55da2cca2f..aabbbc09de1fb 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -312,105 +312,106 @@ <class name="customer-address-form">true</class> </additionalClasses> </settings> - <component name="customer_default_billing_address_content" template="Magento_Customer/default-address"> - <argument name="data" xsi:type="array"> - <item name="config" xsi:type="array"> - <item name="defaultAddressClass" xsi:type="string">billing-address</item> - <item name="title" translate="true" xsi:type="string">Default Billing Address</item> - <item name="contentClass" xsi:type="string">customer-default-billing-address-content</item> - <item name="notExistsMessage" xsi:type="string" translate="true">The customer does not have default billing address</item> - <item name="tracks" xsi:type="array"> - <item name="address" xsi:type="boolean">true</item> - </item> - </item> - </argument> - <settings> - <imports> - <link name="address">${ $.provider}:data.default_billing_address</link> - </imports> - </settings> - </component> - - <button name="edit_billing_address" component="Magento_Customer/js/address/default-address"> - <argument name="data" xsi:type="array"> - <item name="config" xsi:type="array"> - <item name="buttonClasses" xsi:type="string">edit-default-billing-address-button</item> - <item name="actions" xsi:type="array"> - <item name="0" xsi:type="array"> - <item name="targetName" xsi:type="string">${ $.parentName}.customer_address_update_modal.update_customer_address_form_loader</item> - <item name="actionName" xsi:type="string">destroyInserted</item> - </item> - <item name="1" xsi:type="array"> - <item name="targetName" xsi:type="string">${ $.parentName}.customer_address_update_modal</item> - <item name="actionName" xsi:type="string">openModal</item> - </item> - <item name="2" xsi:type="array"> - <item name="targetName" xsi:type="string">${ $.parentName}.customer_address_update_modal.update_customer_address_form_loader</item> - <item name="actionName" xsi:type="string">render</item> + <component name="customer_default_billing_address_wrapper" template="Magento_Customer/default-address-wrapper"> + <component name="customer_default_billing_address_content" template="Magento_Customer/default-address"> + <argument name="data" xsi:type="array"> + <item name="config" xsi:type="array"> + <item name="defaultAddressClass" xsi:type="string">billing-address</item> + <item name="title" translate="true" xsi:type="string">Default Billing Address</item> + <item name="contentClass" xsi:type="string">customer-default-billing-address-content</item> + <item name="notExistsMessage" xsi:type="string" translate="true">The customer does not have default billing address</item> + <item name="tracks" xsi:type="array"> + <item name="address" xsi:type="boolean">true</item> </item> </item> - </item> - </argument> - <settings> - <componentType>button</componentType> - <title translate="true">Edit - true - - ${ $.provider}:data.default_billing_address.entity_id - ${ $.provider}:data.default_billing_address - - - - - - - - shipping-address - Default Shipping Address - customer-default-shipping-address-content - The customer does not have default shipping address - - true + + + + ${ $.provider}:data.default_billing_address + + + + - - + + + button + Edit + true + + ${ $.provider}:data.default_shipping_address.entity_id + ${ $.provider}:data.default_shipping_address + + + + - - + + @@ -411,7 +411,7 @@ - + + + + button + Edit + true + + ${ $.provider}:data.default_billing_address.entity_id + ${ $.provider}:data.default_billing_address + + + + @@ -380,37 +380,37 @@ ${ $.provider}:data.default_shipping_address - - + + + button + Edit + true + + ${ $.provider}:data.default_shipping_address.entity_id + ${ $.provider}:data.default_shipping_address + + + +
- + + + + button + Edit + true + + ${ $.provider}:data.default_billing_address.entity_id + ${ $.provider}:data.default_billing_address + + + @@ -378,39 +378,39 @@ ${ $.provider}:data.default_shipping_address - + efas - - + + + + button + Edit + true + + ${ $.provider}:data.default_shipping_address.entity_id + ${ $.provider}:data.default_shipping_address + + +