diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php index 53f52ef268dd41ff76b339fb44263c5bbec007b4..96ddff93daae882617893e54ea29a7e79f918908 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php @@ -62,7 +62,7 @@ class ReadHandler } } - $value['images'][] = $mediaEntry; + $value['images'][$mediaEntry['value_id']] = $mediaEntry; } $product->setData( diff --git a/app/code/Magento/Catalog/Setup/InstallSchema.php b/app/code/Magento/Catalog/Setup/InstallSchema.php index fd1db944b46e74e126600ddbf9605c61e96e3a8f..2ccd8af79a5da7eb51f43aeabfa19a9de14c81c1 100644 --- a/app/code/Magento/Catalog/Setup/InstallSchema.php +++ b/app/code/Magento/Catalog/Setup/InstallSchema.php @@ -1233,6 +1233,15 @@ class InstallSchema implements InstallSchemaInterface $installer->getIdxName('catalog_category_product', ['product_id']), ['product_id'] ) + ->addIndex( + $installer->getIdxName( + 'catalog_category_product', + ['category_id', 'product_id'], + \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_UNIQUE + ), + ['category_id', 'product_id'], + ['type' => \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_UNIQUE] + ) ->addForeignKey( $installer->getFkName('catalog_category_product', 'product_id', 'catalog_product_entity', 'entity_id'), 'product_id', diff --git a/app/code/Magento/Catalog/Setup/UpgradeSchema.php b/app/code/Magento/Catalog/Setup/UpgradeSchema.php index 756bc19f3d99adc8dd0de0d00e760f236f6a7d41..03f3ac9581729b96eeab485da5be125072f23773 100644 --- a/app/code/Magento/Catalog/Setup/UpgradeSchema.php +++ b/app/code/Magento/Catalog/Setup/UpgradeSchema.php @@ -29,9 +29,30 @@ class UpgradeSchema implements UpgradeSchemaInterface $this->removeGroupPrice($setup); } + if (version_compare($context->getVersion(), '2.0.6', '<')) { + $this->addUniqueKeyToCategoryProductTable($setup); + } $setup->endSetup(); } + /** + * @param SchemaSetupInterface $setup + * @return void + */ + protected function addUniqueKeyToCategoryProductTable(SchemaSetupInterface $setup) + { + $setup->getConnection()->addIndex( + $setup->getTable('catalog_category_product'), + $setup->getIdxName( + 'catalog_category_product', + ['category_id', 'product_id'], + \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_UNIQUE + ), + ['category_id', 'product_id'], + \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_UNIQUE + ); + } + /** * * @param SchemaSetupInterface $setup diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml index 6885f97329526288185aa079d9609e58ff182157..6cb4325dd8ada0df072bd59158b465e06455876e 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml @@ -77,14 +77,15 @@ <?php $values = []; foreach($block->getOptionValues() as $value) { - array_push($values, $value->getData()); + $value = $value->getData(); + $values[] = is_array($value) ? array_map("htmlspecialchars_decode", $value) : $value; } ?> <script type="text/x-magento-init"> { "*": { "Magento_Catalog/js/options": { - "attributesData": <?php /* @escapeNotVerified */ echo json_encode($values); ?>, + "attributesData": <?php /* @noEscape */ echo json_encode($values, JSON_HEX_QUOT); ?>, "isSortable": <?php echo (int)(!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()) ?>, "isReadOnly": <?php echo (int)$block->getReadOnly(); ?> } diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/product-gallery.js b/app/code/Magento/Catalog/view/adminhtml/web/js/product-gallery.js index 454b9da45b2ac626d94dc28f7b8337c27e06aaa4..fe44bcd03604a17e92542cf367eff87c76ac68ff 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/product-gallery.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/product-gallery.js @@ -165,7 +165,7 @@ define([ imgElement; imageData = $.extend({ - 'file_id': Math.random().toString(33).substr(2, 18), + 'file_id': imageData.value_id ? imageData.value_id : Math.random().toString(33).substr(2, 18), 'disabled': imageData.disabled ? imageData.disabled : 0, 'position': count + 1, sizeLabel: bytesToSize(imageData.size) diff --git a/app/code/Magento/Catalog/view/base/web/js/price-options.js b/app/code/Magento/Catalog/view/base/web/js/price-options.js index 3211100440b27171af203065d2fbd58b3fa47ff0..2ac32fbe5d49a49becea34eaf94252bcceda8f8c 100644 --- a/app/code/Magento/Catalog/view/base/web/js/price-options.js +++ b/app/code/Magento/Catalog/view/base/web/js/price-options.js @@ -18,7 +18,7 @@ define([ optionsSelector: '.product-custom-option', optionConfig: {}, optionHandlers: {}, - optionTemplate: '<%- data.label %>' + + optionTemplate: '<%= data.label %>' + '<% if (data.finalPrice.value) { %>' + ' +<%- data.finalPrice.formatted %>' + '<% } %>', diff --git a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php index c91170f62940464e44d7757ebfb543fdf9265bd4..3bb9ef4c4d5c6f847d17365df179f54b0b678752 100644 --- a/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php +++ b/app/code/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStock.php @@ -196,7 +196,7 @@ class DefaultStock extends AbstractIndexer implements StockInterface )->columns(['qty' => new \Zend_Db_Expr('SUM(' . $qtyExpr . ')')]) ->where('cw.website_id != 0') ->where('e.type_id = ?', $this->getTypeId()) - ->group('e.entity_id'); + ->group(['e.entity_id', 'cw.website_id']); // add limitation of status $condition = $connection->quoteInto( diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php index 6307b3df6ad019a6cec3a8e0956da55398120649..9f34070ad4216a703814998d3a226da867dc40ba 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php @@ -99,7 +99,8 @@ class ProductAttributeMediaGalleryManagementInterfaceTest extends \Magento\TestF protected function getTargetGalleryEntryId() { $mediaGallery = $this->getTargetSimpleProduct()->getData('media_gallery'); - return (int)$mediaGallery['images'][0]['value_id']; + $image = array_shift($mediaGallery['images']); + return (int)$image['value_id']; } /** @@ -126,7 +127,7 @@ class ProductAttributeMediaGalleryManagementInterfaceTest extends \Magento\TestF $mediaGallery = $targetProduct->getData('media_gallery'); $this->assertCount(1, $mediaGallery['images']); - $updatedImage = $mediaGallery['images'][0]; + $updatedImage = array_shift($mediaGallery['images']); $this->assertEquals($actualResult, $updatedImage['value_id']); $this->assertEquals('Image Text', $updatedImage['label']); $this->assertEquals(1, $updatedImage['position']); @@ -165,7 +166,7 @@ class ProductAttributeMediaGalleryManagementInterfaceTest extends \Magento\TestF $targetProduct = $this->getTargetSimpleProduct(); $mediaGallery = $targetProduct->getData('media_gallery'); $this->assertCount(1, $mediaGallery['images']); - $updatedImage = $mediaGallery['images'][0]; + $updatedImage = array_shift($mediaGallery['images']); // Values for not default store view were provided $this->assertEquals('Image Text', $updatedImage['label']); $this->assertEquals($actualResult, $updatedImage['value_id']); @@ -207,7 +208,7 @@ class ProductAttributeMediaGalleryManagementInterfaceTest extends \Magento\TestF $this->assertNull($targetProduct->getData('small_image')); $mediaGallery = $targetProduct->getData('media_gallery'); $this->assertCount(1, $mediaGallery['images']); - $updatedImage = $mediaGallery['images'][0]; + $updatedImage = array_shift($mediaGallery['images']); $this->assertEquals('Updated Image Text', $updatedImage['label']); $this->assertEquals('/m/a/magento_image.jpg', $updatedImage['file']); $this->assertEquals(10, $updatedImage['position']); @@ -245,7 +246,7 @@ class ProductAttributeMediaGalleryManagementInterfaceTest extends \Magento\TestF $this->assertNull($targetProduct->getData('small_image')); $mediaGallery = $targetProduct->getData('media_gallery'); $this->assertCount(1, $mediaGallery['images']); - $updatedImage = $mediaGallery['images'][0]; + $updatedImage = array_shift($mediaGallery['images']); // Not default store view values were updated $this->assertEquals('Updated Image Text', $updatedImage['label']); $this->assertEquals('/m/a/magento_image.jpg', $updatedImage['file']); @@ -406,7 +407,7 @@ class ProductAttributeMediaGalleryManagementInterfaceTest extends \Magento\TestF public function testUpdateThrowsExceptionIfTargetProductDoesNotExist() { $this->updateServiceInfo['rest']['resourcePath'] = '/V1/products/wrong_product_sku/media' - . '/' . $this->getTargetGalleryEntryId(); + . '/' . 'wrong-sku'; $requestData = [ 'sku' => 'wrong_product_sku', 'entry' => [ diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/NavigateRelatedProductsTest.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/NavigateRelatedProductsTest.php index 6b7aa9b8c902435428379024c210b7e2ba42791d..53451339e075ada19cf8bd08e04c48e285783623 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/NavigateRelatedProductsTest.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/NavigateRelatedProductsTest.php @@ -70,7 +70,6 @@ class NavigateRelatedProductsTest extends AbstractProductPromotedProductsTest $productsToVerify, CheckoutCart $checkoutCart ) { - $this->markTestIncomplete('MAGETWO-50178'); // Preconditions $this->createProducts($products); $this->assignPromotedProducts($promotedProducts, 'related_products'); diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml index 146a78a9d281995768fa2ffa154f09047656bba6..d117089988022ddb9a982c9148072c1f5f7d39cd 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml @@ -128,6 +128,10 @@ <field name="checkout_data" xsi:type="array"> <item name="dataset" xsi:type="string">configurable_default</item> </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">40</item> + <item name="dataset" xsi:type="string">price_40</item> + </field> </dataset> <dataset name="product_with_color_and_size"> @@ -156,6 +160,10 @@ <field name="checkout_data" xsi:type="array"> <item name="dataset" xsi:type="string">configurable_default</item> </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">40</item> + <item name="dataset" xsi:type="string">price_40</item> + </field> </dataset> <dataset name="one_variation"> @@ -181,6 +189,10 @@ <field name="attribute_set_id" xsi:type="array"> <item name="dataset" xsi:type="string">default</item> </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">40</item> + <item name="dataset" xsi:type="string">price_40</item> + </field> </dataset> <dataset name="not_virtual_for_type_switching"> @@ -238,6 +250,10 @@ <field name="checkout_data" xsi:type="array"> <item name="dataset" xsi:type="string">configurable_one_option</item> </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">40</item> + <item name="dataset" xsi:type="string">price_40</item> + </field> </dataset> <dataset name="with_out_of_stock_item"> @@ -263,6 +279,10 @@ <field name="attribute_set_id" xsi:type="array"> <item name="dataset" xsi:type="string">default</item> </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">40</item> + <item name="dataset" xsi:type="string">price_40</item> + </field> </dataset> <dataset name="two_options_with_fixed_price"> @@ -293,6 +313,10 @@ <field name="checkout_data" xsi:type="array"> <item name="dataset" xsi:type="string">configurable_two_options_with_fixed_price</item> </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">40</item> + <item name="dataset" xsi:type="string">price_40</item> + </field> </dataset> <dataset name="two_variations_with_fixed_price"> @@ -316,6 +340,10 @@ <field name="checkout_data" xsi:type="array"> <item name="dataset" xsi:type="string">configurable_two_options_by_one_dollar</item> </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">40</item> + <item name="dataset" xsi:type="string">price_40</item> + </field> </dataset> <dataset name="filterable_two_options_with_zero_price"> @@ -342,6 +370,10 @@ <field name="attribute_set_id" xsi:type="array"> <item name="dataset" xsi:type="string">custom_attribute_set</item> </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">40</item> + <item name="dataset" xsi:type="string">price_40</item> + </field> </dataset> <dataset name="two_options_by_one_dollar"> @@ -372,6 +404,10 @@ <field name="checkout_data" xsi:type="array"> <item name="dataset" xsi:type="string">configurable_two_options_by_one_dollar</item> </field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">40</item> + <item name="dataset" xsi:type="string">price_40</item> + </field> </dataset> </repository> </config> diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php index f5cbed9d044ef3a2f4231c0a0527025b9154aa4e..739c225c9712109f56b255940964043e1eaf4944 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/ReadHandlerTest.php @@ -64,10 +64,11 @@ class ReadHandlerTest extends \PHPUnit_Framework_TestCase $this->assertArrayHasKey('media_gallery', $data); $this->assertArrayHasKey('images', $data['media_gallery']); + $image = array_shift($data['media_gallery']['images']); $this->assertEquals( 'Image Alt Text', - $data['media_gallery']['images'][0]['label'] + $image['label'] ); } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStockTest.php b/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStockTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8fbcad5267cf774401910a63b7dcf7b2c8bcd427 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogInventory/Model/ResourceModel/Indexer/Stock/DefaultStockTest.php @@ -0,0 +1,67 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock; + +/** + * Class DefaultStockTest + * @magentoAppArea adminhtml + */ +class DefaultStockTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock + */ + private $indexer; + + protected function setUp() + { + $this->indexer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock::class + ); + + } + + /** + * @magentoDataFixture Magento/Store/_files/website.php + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testReindexEntity() + { + /** @var \Magento\Catalog\Model\ProductRepository $productRepository */ + $productRepository = $this->getObject(\Magento\Catalog\Model\ProductRepository::class); + /** @var \Magento\Store\Api\WebsiteRepositoryInterface $websiteRepository */ + $websiteRepository = $this->getObject( + \Magento\Store\Api\WebsiteRepositoryInterface::class + ); + $product = $productRepository->get('simple'); + $testWebsite = $websiteRepository->get('test'); + $product->setWebsiteIds([1, $testWebsite->getId()])->save(); + + /** @var \Magento\CatalogInventory\Api\StockStatusCriteriaInterfaceFactory $criteriaFactory */ + $criteriaFactory = $this->getObject( + \Magento\CatalogInventory\Api\StockStatusCriteriaInterfaceFactory::class + ); + /** @var \Magento\CatalogInventory\Api\StockStatusRepositoryInterface $stockStatusRepository */ + $stockStatusRepository = $this->getObject( + \Magento\CatalogInventory\Api\StockStatusRepositoryInterface::class + ); + $criteria = $criteriaFactory->create(); + $criteria->setProductsFilter([$product->getId()]); + $criteria->addFilter('website', 'website_id', $testWebsite->getId()); + $items = $stockStatusRepository->getList($criteria)->getItems(); + $this->assertEquals($product->getId(), $items[$product->getId()]->getProductId()); + } + + private function getObject($class) + { + return \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + $class + ); + } +}