From 0db2b08cb125046077b0abf1039718a3f6805cf9 Mon Sep 17 00:00:00 2001 From: magento-engcom-team Date: Fri, 3 Nov 2017 15:52:19 +0200 Subject: [PATCH 1/6] 11941: Invoice for products that use qty decimal rounds down to whole number. --- .../Observer/AddStockItemsObserver.php | 77 +++++++ .../Observer/AddStockItemsObserverTest.php | 165 +++++++++++++++ .../Magento/CatalogInventory/etc/events.xml | 3 + .../product_simple_with_decimal_qty.php | 192 ++++++++++++++++++ ...oduct_simple_with_decimal_qty_rollback.php | 26 +++ .../Observer/AddStockItemsObserverTest.php | 41 ++++ .../Sales/Model/AdminOrder/CreateTest.php | 6 +- 7 files changed, 509 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/CatalogInventory/Observer/AddStockItemsObserver.php create mode 100644 app/code/Magento/CatalogInventory/Test/Unit/Observer/AddStockItemsObserverTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_decimal_qty.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_decimal_qty_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogInventory/Observer/AddStockItemsObserverTest.php diff --git a/app/code/Magento/CatalogInventory/Observer/AddStockItemsObserver.php b/app/code/Magento/CatalogInventory/Observer/AddStockItemsObserver.php new file mode 100644 index 0000000000000..8fa90cf6531c4 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Observer/AddStockItemsObserver.php @@ -0,0 +1,77 @@ +criteriaInterfaceFactory = $criteriaInterfaceFactory; + $this->stockItemRepository = $stockItemRepository; + $this->stockConfiguration = $stockConfiguration; + } + + /** + * Add stock items to products in collection. + * + * @param Observer $observer + * @return void + */ + public function execute(Observer $observer) + { + /** @var Collection $productCollection */ + $productCollection = $observer->getData('collection'); + $productIds = array_keys($productCollection->getItems()); + $criteria = $this->criteriaInterfaceFactory->create(); + $criteria->setProductsFilter($productIds); + $criteria->setScopeFilter($this->stockConfiguration->getDefaultScopeId()); + $stockItemCollection = $this->stockItemRepository->getList($criteria); + foreach ($stockItemCollection->getItems() as $item) { + /** @var Product $product */ + $product = $productCollection->getItemById($item->getProductId()); + $productExtension = $product->getExtensionAttributes(); + $productExtension->setStockItem($item); + $product->setExtensionAttributes($productExtension); + } + } +} diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Observer/AddStockItemsObserverTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Observer/AddStockItemsObserverTest.php new file mode 100644 index 0000000000000..8de05bd014039 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Unit/Observer/AddStockItemsObserverTest.php @@ -0,0 +1,165 @@ +criteriaInterfaceFactoryMock = $this->getMockBuilder(StockItemCriteriaInterfaceFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->stockItemRepositoryMock = $this->getMockBuilder(StockItemRepositoryInterface::class) + ->setMethods(['getList']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->stockConfigurationMock = $this->getMockBuilder(StockConfigurationInterface::class) + ->setMethods(['getDefaultScopeId']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->subject = $objectManager->getObject( + AddStockItemsObserver::class, + [ + 'criteriaInterfaceFactory' => $this->criteriaInterfaceFactoryMock, + 'stockItemRepository' => $this->stockItemRepositoryMock, + 'stockConfiguration' => $this->stockConfigurationMock + ] + ); + } + + /** + * Test AddStockItemsObserver::execute() add stock item to product as extension attribute. + */ + public function testExecute() + { + $productId = 1; + $defaultScopeId = 0; + + $criteria = $this->getMockBuilder(StockItemCriteriaInterface::class) + ->setMethods(['setProductsFilter', 'setScopeFilter']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $criteria->expects(self::once()) + ->method('setProductsFilter') + ->with(self::identicalTo([$productId])) + ->willReturn(true); + $criteria->expects(self::once()) + ->method('setScopeFilter') + ->with(self::identicalTo($defaultScopeId)) + ->willReturn(true); + + $this->criteriaInterfaceFactoryMock->expects(self::once()) + ->method('create') + ->willReturn($criteria); + $stockItemCollection = $this->getMockBuilder(StockItemCollectionInterface::class) + ->setMethods(['getItems']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $stockItem = $this->getMockBuilder(StockItemInterface::class) + ->setMethods(['getProductId']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $stockItem->expects(self::once()) + ->method('getProductId') + ->willReturn($productId); + + $stockItemCollection->expects(self::once()) + ->method('getItems') + ->willReturn([$stockItem]); + + $this->stockItemRepositoryMock->expects(self::once()) + ->method('getList') + ->with(self::identicalTo($criteria)) + ->willReturn($stockItemCollection); + + $this->stockConfigurationMock->expects(self::once()) + ->method('getDefaultScopeId') + ->willReturn($defaultScopeId); + + $productExtension = $this->getMockBuilder(ProductExtensionInterface::class) + ->setMethods(['setStockItem']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $productExtension->expects(self::once()) + ->method('setStockItem') + ->with(self::identicalTo($stockItem)); + + $product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $product->expects(self::once()) + ->method('getExtensionAttributes') + ->willReturn($productExtension); + $product->expects(self::once()) + ->method('setExtensionAttributes') + ->with(self::identicalTo($productExtension)) + ->willReturnSelf(); + + /** @var ProductCollection|\PHPUnit_Framework_MockObject_MockObject $productCollection */ + $productCollection = $this->getMockBuilder(ProductCollection::class) + ->disableOriginalConstructor() + ->getMock(); + $productCollection->expects(self::once()) + ->method('getItems') + ->willReturn([$productId => $product]); + $productCollection->expects(self::once()) + ->method('getItemById') + ->with(self::identicalTo($productId)) + ->willReturn($product); + + /** @var Observer|\PHPUnit_Framework_MockObject_MockObject $observer */ + $observer = $this->getMockBuilder(Observer::class) + ->disableOriginalConstructor() + ->getMock(); + $observer->expects(self::once()) + ->method('getData') + ->with('collection') + ->willReturn($productCollection); + + $this->subject->execute($observer); + } +} diff --git a/app/code/Magento/CatalogInventory/etc/events.xml b/app/code/Magento/CatalogInventory/etc/events.xml index 3b5f2483ec57e..0a9f3c2d40dca 100644 --- a/app/code/Magento/CatalogInventory/etc/events.xml +++ b/app/code/Magento/CatalogInventory/etc/events.xml @@ -42,4 +42,7 @@ + + + diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_decimal_qty.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_decimal_qty.php new file mode 100644 index 0000000000000..37ce93cc9c420 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_decimal_qty.php @@ -0,0 +1,192 @@ +reinitialize(); + +/** @var \Magento\TestFramework\ObjectManager $objectManager */ +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Catalog\Api\CategoryLinkManagementInterface $categoryLinkManagement */ +$categoryLinkManagement = $objectManager->get(\Magento\Catalog\Api\CategoryLinkManagementInterface::class); + +$tierPrices = []; +/** @var \Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory $tierPriceFactory */ +$tierPriceFactory = $objectManager->get(\Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory::class); +/** @var $tpExtensionAttributes */ +$tpExtensionAttributesFactory = $objectManager->get(ProductTierPriceExtensionFactory::class); + +$adminWebsite = $objectManager->get(\Magento\Store\Api\WebsiteRepositoryInterface::class)->get('admin'); +$tierPriceExtensionAttributes1 = $tpExtensionAttributesFactory->create() + ->setWebsiteId($adminWebsite->getId()); + +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, + 'qty' => 2, + 'value' => 8, + ], + ] +)->setExtensionAttributes($tierPriceExtensionAttributes1); + +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, + 'qty' => 5, + 'value' => 5, + ], + ] +)->setExtensionAttributes($tierPriceExtensionAttributes1); + +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => \Magento\Customer\Model\Group::NOT_LOGGED_IN_ID, + 'qty' => 3, + 'value' => 5, + ], + ] +)->setExtensionAttributes($tierPriceExtensionAttributes1); + +$tierPriceExtensionAttributes2 = $tpExtensionAttributesFactory->create() + ->setWebsiteId($adminWebsite->getId()) + ->setPercentageValue(50); + +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => \Magento\Customer\Model\Group::NOT_LOGGED_IN_ID, + 'qty' => 10, + ], + ] +)->setExtensionAttributes($tierPriceExtensionAttributes2); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = $objectManager->create(\Magento\Catalog\Model\Product::class); +$product->isObjectNew(true); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setId(1) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product') + ->setSku('simple_with_decimal_qty') + ->setPrice(10) + ->setWeight(1) + ->setShortDescription("Short description") + ->setTaxClassId(0) + ->setTierPrices($tierPrices) + ->setDescription('Description with html tag') + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 1, + 'is_in_stock' => 1, + ] + )->setCanSaveCustomOptions(true) + ->setHasOptions(true); + +$oldOptions = [ + [ + 'previous_group' => 'text', + 'title' => 'Test Field', + 'type' => 'field', + 'is_require' => 1, + 'sort_order' => 0, + 'price' => 1, + 'price_type' => 'fixed', + 'sku' => '1-text', + 'max_characters' => 100, + ], + [ + 'previous_group' => 'date', + 'title' => 'Test Date and Time', + 'type' => 'date_time', + 'is_require' => 1, + 'sort_order' => 0, + 'price' => 2, + 'price_type' => 'fixed', + 'sku' => '2-date', + ], + [ + 'previous_group' => 'select', + 'title' => 'Test Select', + 'type' => 'drop_down', + 'is_require' => 1, + 'sort_order' => 0, + 'values' => [ + [ + 'option_type_id' => null, + 'title' => 'Option 1', + 'price' => 3, + 'price_type' => 'fixed', + 'sku' => '3-1-select', + ], + [ + 'option_type_id' => null, + 'title' => 'Option 2', + 'price' => 3, + 'price_type' => 'fixed', + 'sku' => '3-2-select', + ], + ], + ], + [ + 'previous_group' => 'select', + 'title' => 'Test Radio', + 'type' => 'radio', + 'is_require' => 1, + 'sort_order' => 0, + 'values' => [ + [ + 'option_type_id' => null, + 'title' => 'Option 1', + 'price' => 3, + 'price_type' => 'fixed', + 'sku' => '4-1-radio', + ], + [ + 'option_type_id' => null, + 'title' => 'Option 2', + 'price' => 3, + 'price_type' => 'fixed', + 'sku' => '4-2-radio', + ], + ], + ], +]; + +$options = []; + +/** @var \Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory $customOptionFactory */ +$customOptionFactory = $objectManager->create(\Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory::class); + +foreach ($oldOptions as $option) { + /** @var \Magento\Catalog\Api\Data\ProductCustomOptionInterface $option */ + $option = $customOptionFactory->create(['data' => $option]); + $option->setProductSku($product->getSku()); + + $options[] = $option; +} + +$product->setOptions($options); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepositoryFactory */ +$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +$productRepository->save($product); + +$categoryLinkManagement->assignProductToCategories( + $product->getSku(), + [2] +); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_decimal_qty_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_decimal_qty_rollback.php new file mode 100644 index 0000000000000..55bd53a2d5794 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_decimal_qty_rollback.php @@ -0,0 +1,26 @@ +getInstance()->reinitialize(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class); +try { + $product = $productRepository->get('simple_with_decimal_qty', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CatalogInventory/Observer/AddStockItemsObserverTest.php b/dev/tests/integration/testsuite/Magento/CatalogInventory/Observer/AddStockItemsObserverTest.php new file mode 100644 index 0000000000000..71af5d102a8d7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogInventory/Observer/AddStockItemsObserverTest.php @@ -0,0 +1,41 @@ +create(Quote::class); + $quote->load('test01', 'reserved_order_id'); + /** @var CollectionFactory $collectionFactory */ + $collectionFactory = Bootstrap::getObjectManager()->create(CollectionFactory::class); + /** @var Collection $collection */ + $collection = $collectionFactory->create(); + $collection->setQuote($quote); + /** @var Quote\Item $quoteItem */ + foreach ($collection->getItems() as $quoteItem) { + self::assertNotEmpty($quoteItem->getProduct()->getExtensionAttributes()->getStockItem()); + self::assertInstanceOf( + StockItemInterface::class, + $quoteItem->getProduct()->getExtensionAttributes()->getStockItem() + ); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/AdminOrder/CreateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/AdminOrder/CreateTest.php index a4dac0f285f58..28b2575643b05 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/AdminOrder/CreateTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/AdminOrder/CreateTest.php @@ -390,7 +390,7 @@ public function testCreateOrderNewCustomerDifferentAddresses() } /** - * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_decimal_qty.php * @magentoDbIsolation enabled * @magentoAppIsolation enabled */ @@ -421,6 +421,10 @@ public function testCreateOrderNewCustomer() $paymentMethod ); $order = $this->_model->createOrder(); + //Check, order considering decimal qty in product. + foreach ($order->getItems() as $orderItem) { + self::assertTrue($orderItem->getIsQtyDecimal()); + } $this->_verifyCreatedOrder($order, $shippingMethod); } From 41e9ec05e7b7330ff0ee0d278e1012527d9889b5 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky Date: Wed, 15 Nov 2017 14:43:29 +0200 Subject: [PATCH 2/6] magento/magento2#12083: Cannot import zero (0) value into custom attribute --- .../Import/Product/Type/AbstractType.php | 2 +- .../Import/Product/Type/AbstractTest.php | 74 ++++++++++++++----- .../Model/Import/_files/custom_attributes.php | 40 ++++++++++ .../_files/custom_attributes_rollback.php | 20 +++++ 4 files changed, 118 insertions(+), 18 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/custom_attributes.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/custom_attributes_rollback.php diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php index 5681b1aa6607d..939d6b2de67ee 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php @@ -534,7 +534,7 @@ public function prepareAttributesWithDefaultValueForSave(array $rowData, $withDe public function clearEmptyData(array $rowData) { foreach ($this->_getProductAttributes($rowData) as $attrCode => $attrParams) { - if (!$attrParams['is_static'] && empty($rowData[$attrCode])) { + if (!$attrParams['is_static'] && !isset($rowData[$attrCode])) { unset($rowData[$attrCode]); } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractTest.php index 8860c12f0f983..ff07963d0e3ad 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractTest.php @@ -12,20 +12,25 @@ class AbstractTest extends \PHPUnit\Framework\TestCase */ protected $_model; + /** + * @var \Magento\TestFramework\ObjectManager + */ + private $objectManager; + /** * On product import abstract class methods level it doesn't matter what product type is using. * That is why current tests are using simple product entity type by default */ protected function setUp() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $params = [$objectManager->create(\Magento\CatalogImportExport\Model\Import\Product::class), 'simple']; + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $params = [$this->objectManager->create(\Magento\CatalogImportExport\Model\Import\Product::class), 'simple']; $this->_model = $this->getMockForAbstractClass( \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType::class, [ - $objectManager->get(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory::class), - $objectManager->get(\Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory::class), - $objectManager->get(\Magento\Framework\App\ResourceConnection::class), + $this->objectManager->get(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory::class), + $this->objectManager->get(\Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory::class), + $this->objectManager->get(\Magento\Framework\App\ResourceConnection::class), $params ] ); @@ -130,6 +135,11 @@ public function prepareAttributesWithDefaultValueForSaveDataProvider() } /** + * Test cleaning imported attribute data from empty values (note '0' is not empty). + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/CatalogImportExport/Model/Import/_files/custom_attributes.php * @dataProvider clearEmptyDataDataProvider */ public function testClearEmptyData($rowData, $expectedAttributes) @@ -141,8 +151,14 @@ public function testClearEmptyData($rowData, $expectedAttributes) } } + /** + * Data provider for testClearEmptyData. + * + * @return array + */ public function clearEmptyDataDataProvider() { + // We use sku attribute to test static attributes. return [ [ [ @@ -152,6 +168,7 @@ public function clearEmptyDataDataProvider() 'product_type' => 'simple', 'name' => 'Simple 01', 'price' => 10, + 'test_attribute' => '1', ], [ 'sku' => 'simple1', @@ -159,26 +176,49 @@ public function clearEmptyDataDataProvider() '_attribute_set' => 'Default', 'product_type' => 'simple', 'name' => 'Simple 01', - 'price' => 10 + 'price' => 10, + 'test_attribute' => '1', ], ], [ [ - 'sku' => '', - 'store_view_code' => 'German', + 'sku' => '0', + 'store_view_code' => '', '_attribute_set' => 'Default', - 'product_type' => '', - 'name' => 'Simple 01 German', - 'price' => '', + 'product_type' => 'simple', + 'name' => 'Simple 01', + 'price' => 10, + 'test_attribute' => '0', ], [ - 'sku' => '', - 'store_view_code' => 'German', + 'sku' => '0', + 'store_view_code' => '', '_attribute_set' => 'Default', - 'product_type' => '', - 'name' => 'Simple 01 German' - ] - ] + 'product_type' => 'simple', + 'name' => 'Simple 01', + 'price' => 10, + 'test_attribute' => '0', + ], + ], + [ + [ + 'sku' => null, + 'store_view_code' => '', + '_attribute_set' => 'Default', + 'product_type' => 'simple', + 'name' => 'Simple 01', + 'price' => 10, + 'test_attribute' => null, + ], + [ + 'sku' => null, + 'store_view_code' => '', + '_attribute_set' => 'Default', + 'product_type' => 'simple', + 'name' => 'Simple 01', + 'price' => 10, + ], + ], ]; } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/custom_attributes.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/custom_attributes.php new file mode 100644 index 0000000000000..82c22594f30aa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/custom_attributes.php @@ -0,0 +1,40 @@ +create(\Magento\Eav\Model\Entity\Type::class); +$entityType->loadByCode('catalog_product'); +$entityTypeId = $entityType->getId(); + +/** @var \Magento\Eav\Model\Entity\Attribute\Set $attributeSet */ +$attributeSet = $objectManager->create(\Magento\Eav\Model\Entity\Attribute\Set::class); +$attributeSet->load('default', 'attribute_set_name'); +$attributeSetId = $attributeSet->getId(); + +$attributeGroupId = $attributeSet->getDefaultGroupId($entityType->getDefaultAttributeSetId()); + +$attributeData = [ + [ + 'attribute_code' => 'test_attribute', + 'entity_type_id' => $entityTypeId, + 'backend_type' => 'varchar', + 'is_required' => 1, + 'is_user_defined' => 1, + 'is_unique' => 0, + 'attribute_set_id' => $attributeSetId, + 'attribute_group_id' => $attributeGroupId, + ], +]; + +foreach ($attributeData as $data) { + /** @var \Magento\Eav\Model\Entity\Attribute $attribute */ + $attribute = $objectManager->create(\Magento\Eav\Model\Entity\Attribute::class); + $attribute->setData($data); + $attribute->setIsStatic(true); + $attribute->save(); +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/custom_attributes_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/custom_attributes_rollback.php new file mode 100644 index 0000000000000..f3afb096347ed --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/custom_attributes_rollback.php @@ -0,0 +1,20 @@ +create(\Magento\Eav\Model\Entity\Attribute::class); + $attribute->loadByCode('catalog_product', $attributeCode); + if ($attribute->getId()) { + $attribute->delete(); + } +} From 89d613c1da1c7a054fc256df3fda206ff32bd458 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky Date: Wed, 15 Nov 2017 15:23:59 +0200 Subject: [PATCH 3/6] magento/magento2#12083: Cannot import zero (0) value into custom attribute --- .../Model/Import/Product/Type/AbstractTest.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractTest.php index ff07963d0e3ad..862ecb4cbe028 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractTest.php @@ -28,9 +28,15 @@ protected function setUp() $this->_model = $this->getMockForAbstractClass( \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType::class, [ - $this->objectManager->get(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory::class), - $this->objectManager->get(\Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory::class), - $this->objectManager->get(\Magento\Framework\App\ResourceConnection::class), + $this->objectManager->get( + \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory::class + ), + $this->objectManager->get( + \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory::class + ), + $this->objectManager->get( + \Magento\Framework\App\ResourceConnection::class + ), $params ] ); From addda4281d079e1b9988ee6fc97d7d79f092acfd Mon Sep 17 00:00:00 2001 From: Oleksandr Yeremenko Date: Thu, 16 Nov 2017 03:19:04 +0100 Subject: [PATCH 4/6] Issue: 3596. Resolve Notice with undefined index 'value' while preview order with instance payable method selected if custom offline payment method is defined in system --- app/code/Magento/Payment/Helper/Data.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Payment/Helper/Data.php b/app/code/Magento/Payment/Helper/Data.php index e3122913d5dfa..f3565ea324290 100644 --- a/app/code/Magento/Payment/Helper/Data.php +++ b/app/code/Magento/Payment/Helper/Data.php @@ -293,6 +293,7 @@ public function getPaymentMethodList($sorted = true, $asLabelValue = false, $wit foreach ($methods as $code => $title) { if (isset($groups[$code])) { $labelValues[$code]['label'] = $title; + $labelValues[$code]['value'] = null; } elseif (isset($groupRelations[$code])) { unset($labelValues[$code]); $labelValues[$groupRelations[$code]]['value'][$code] = ['value' => $code, 'label' => $title]; From ba47d85ec578e7635eb8db76d58d2467a5b52a4e Mon Sep 17 00:00:00 2001 From: RomanKis Date: Wed, 15 Nov 2017 16:11:00 +0200 Subject: [PATCH 5/6] 9764: exception message is wrong and misleading in findAccessorMethodName() of Magento\Framework\Reflection\NameFinder --- .../Framework/Reflection/NameFinder.php | 3 ++- .../Reflection/Test/Unit/NameFinderTest.php | 26 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/Reflection/NameFinder.php b/lib/internal/Magento/Framework/Reflection/NameFinder.php index 97f4b2f81c128..5cd4ab744bb1b 100644 --- a/lib/internal/Magento/Framework/Reflection/NameFinder.php +++ b/lib/internal/Magento/Framework/Reflection/NameFinder.php @@ -99,8 +99,9 @@ public function findAccessorMethodName( } else { throw new \LogicException( sprintf( - 'Property "%s" does not have corresponding setter in class "%s".', + 'Property "%s" does not have accessor method "%s" in class "%s".', $camelCaseProperty, + $accessorName, $class->getName() ) ); diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php index 9ddc6d6aa0c9a..ccf81305df6eb 100644 --- a/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php @@ -37,7 +37,9 @@ public function testGetSetterMethodName() /** * @expectedException \Exception - * @expectedExceptionMessageRegExp /Property "InvalidAttribute" does not have corresponding setter in class (.*?)/ + * @codingStandardsIgnoreStart + * @expectedExceptionMessage Property "InvalidAttribute" does not have accessor method "setInvalidAttribute" in class "Magento\Framework\Reflection\Test\Unit\DataObject" + * @codingStandardsIgnoreEnd */ public function testGetSetterMethodNameInvalidAttribute() { @@ -47,11 +49,31 @@ public function testGetSetterMethodNameInvalidAttribute() /** * @expectedException \Exception - * @expectedExceptionMessageRegExp /Property "ActivE" does not have corresponding setter in class (.*?)/ + * @codingStandardsIgnoreStart + * @expectedExceptionMessage Property "ActivE" does not have accessor method "setActivE" in class "Magento\Framework\Reflection\Test\Unit\DataObject" + * @codingStandardsIgnoreEnd */ public function testGetSetterMethodNameWrongCamelCasedAttribute() { $class = new ClassReflection("\\Magento\\Framework\\Reflection\\Test\\Unit\\DataObject"); $this->nameFinder->getSetterMethodName($class, 'ActivE'); } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Property "Property" does not have accessor method "getProperty" in class "className". + */ + public function testFindAccessorMethodName() + { + $reflectionClass = $this->createMock(\Zend\Code\Reflection\ClassReflection::class); + $reflectionClass->expects($this->atLeastOnce())->method('hasMethod')->willReturn(false); + $reflectionClass->expects($this->atLeastOnce())->method('getName')->willReturn('className'); + + $this->nameFinder->findAccessorMethodName( + $reflectionClass, + 'Property', + 'getProperty', + 'isProperty' + ); + } } From 0a76034167a2f92e1d066014f899e899d2e42768 Mon Sep 17 00:00:00 2001 From: RomanKis Date: Thu, 16 Nov 2017 11:14:21 +0200 Subject: [PATCH 6/6] 9764: exception message is wrong and misleading in findAccessorMethodName() of Magento\Framework\Reflection\NameFinder --- .../Framework/Reflection/Test/Unit/NameFinderTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php index ccf81305df6eb..a467c4b7b5aad 100644 --- a/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php +++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/NameFinderTest.php @@ -27,7 +27,7 @@ protected function setUp() public function testGetSetterMethodName() { - $class = new ClassReflection("\\Magento\\Framework\\Reflection\\Test\\Unit\\DataObject"); + $class = new ClassReflection(\Magento\Framework\Reflection\Test\Unit\DataObject::class); $setterName = $this->nameFinder->getSetterMethodName($class, 'AttrName'); $this->assertEquals("setAttrName", $setterName); @@ -43,7 +43,7 @@ public function testGetSetterMethodName() */ public function testGetSetterMethodNameInvalidAttribute() { - $class = new ClassReflection("\\Magento\\Framework\\Reflection\\Test\\Unit\\DataObject"); + $class = new ClassReflection(\Magento\Framework\Reflection\Test\Unit\DataObject::class); $this->nameFinder->getSetterMethodName($class, 'InvalidAttribute'); } @@ -55,7 +55,7 @@ public function testGetSetterMethodNameInvalidAttribute() */ public function testGetSetterMethodNameWrongCamelCasedAttribute() { - $class = new ClassReflection("\\Magento\\Framework\\Reflection\\Test\\Unit\\DataObject"); + $class = new ClassReflection(\Magento\Framework\Reflection\Test\Unit\DataObject::class); $this->nameFinder->getSetterMethodName($class, 'ActivE'); }