diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category.php index 63a9842808a71..366202df27ee7 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category.php @@ -17,6 +17,11 @@ abstract class Category extends \Magento\Backend\App\Action */ const ADMIN_RESOURCE = 'Magento_Catalog::categories'; + /** + * @var \Magento\Framework\Stdlib\DateTime\Filter\DateTime + */ + private $dateTimeFilter; + /** * Initialize requested category and put it into registry. * Root category can be returned, if inappropriate store/category is specified @@ -107,4 +112,41 @@ protected function ajaxRequestResponse($category, $resultPage) $resultJson->setData($eventResponse->getData()); return $resultJson; } + + /** + * @return \Magento\Framework\Stdlib\DateTime\Filter\DateTime + * + * @deprecated + */ + private function getDateTimeFilter() + { + if ($this->dateTimeFilter === null) { + $this->dateTimeFilter = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Stdlib\DateTime\Filter\DateTime::class); + } + return $this->dateTimeFilter; + } + + /** + * Datetime data preprocessing + * + * @param \Magento\Catalog\Model\Category $category + * @param array $postData + * + * @return array + */ + protected function dateTimePreprocessing($category, $postData) + { + $dateFieldFilters = []; + $attributes = $category->getAttributes(); + foreach ($attributes as $attrKey => $attribute) { + if ($attribute->getBackend()->getType() == 'datetime') { + if (array_key_exists($attrKey, $postData) && $postData[$attrKey] != '') { + $dateFieldFilters[$attrKey] = $this->getDateTimeFilter(); + } + } + } + $inputFilter = new \Zend_Filter_Input($dateFieldFilters, [], $postData); + return $inputFilter->getUnescaped(); + } } diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Add.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Add.php index 17ed8b762c1e4..c66fe32bcce2d 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Add.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Add.php @@ -50,6 +50,15 @@ public function execute() return $resultRedirect->setPath('catalog/*/', ['_current' => true, 'id' => null]); } + /** + * Check if there are data in session (if there was an exception on saving category) + */ + $categoryData = $this->_getSession()->getCategoryData(true); + if (is_array($categoryData)) { + unset($categoryData['image']); + $category->addData($categoryData); + } + $resultPageFactory = $this->_objectManager->get('Magento\Framework\View\Result\PageFactory'); /** @var \Magento\Backend\Model\View\Result\Page $resultPage */ $resultPage = $resultPageFactory->create(); diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Edit.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Edit.php index 3f81b2bbd0ea1..1f5a68969037a 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Edit.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Edit.php @@ -80,16 +80,16 @@ public function execute() } /** - * Check if we have data in session (if during category save was exception) + * Check if there are data in session (if there was an exception on saving category) */ - $data = $this->_getSession()->getCategoryData(true); - if (isset($data['general'])) { - if (isset($data['general']['image']['delete'])) { - $data['general']['image'] = null; + $categoryData = $this->_getSession()->getCategoryData(true); + if (is_array($categoryData)) { + if (isset($categoryData['image']['delete'])) { + $categoryData['image'] = null; } else { - unset($data['general']['image']); + unset($categoryData['image']); } - $category->addData($data['general']); + $category->addData($categoryData); } /** @var \Magento\Backend\Model\View\Result\Page $resultPage */ diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php index 852c465b1482a..d3878e6eba120 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Category/Save.php @@ -9,7 +9,7 @@ /** * Class Save - * + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Save extends \Magento\Catalog\Controller\Adminhtml\Category @@ -34,15 +34,13 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Category * @var array */ protected $stringToBoolInputs = [ - 'general' => [ - 'custom_use_parent_settings', - 'custom_apply_to_products', - 'is_active', - 'include_in_menu', - 'is_anchor', - 'use_default' => ['url_key'], - 'use_config' => ['available_sort_by', 'filter_price_range', 'default_sort_by'] - ] + 'custom_use_parent_settings', + 'custom_apply_to_products', + 'is_active', + 'include_in_menu', + 'is_anchor', + 'use_default' => ['url_key'], + 'use_config' => ['available_sort_by', 'filter_price_range', 'default_sort_by'] ]; /** @@ -110,15 +108,18 @@ public function execute() } $data['general'] = $this->getRequest()->getPostValue(); - $isNewCategory = !isset($data['general']['entity_id']); - $data = $this->stringToBoolConverting($data); - $data = $this->imagePreprocessing($data); - $storeId = isset($data['general']['store_id']) ? $data['general']['store_id'] : null; + $categoryPostData = $data['general']; + + $isNewCategory = !isset($categoryPostData['entity_id']); + $categoryPostData = $this->stringToBoolConverting($categoryPostData); + $categoryPostData = $this->imagePreprocessing($categoryPostData); + $categoryPostData = $this->dateTimePreprocessing($category, $categoryPostData); + $storeId = isset($categoryPostData['store_id']) ? $categoryPostData['store_id'] : null; $store = $this->storeManager->getStore($storeId); $this->storeManager->setCurrentStore($store->getCode()); - $parentId = isset($data['general']['parent']) ? $data['general']['parent'] : null; - if ($data['general']) { - $category->addData($this->_filterCategoryPostData($data['general'])); + $parentId = isset($categoryPostData['parent']) ? $categoryPostData['parent'] : null; + if ($categoryPostData) { + $category->addData($this->_filterCategoryPostData($categoryPostData)); if ($isNewCategory) { $parentCategory = $this->getParentCategory($parentId, $storeId); $category->setPath($parentCategory->getPath()); @@ -128,10 +129,10 @@ public function execute() /** * Process "Use Config Settings" checkboxes */ - $generalPost = $data['general']; + $useConfig = []; - if (isset($generalPost['use_config']) && !empty($generalPost['use_config'])) { - foreach ($generalPost['use_config'] as $attributeCode => $attributeValue) { + if (isset($categoryPostData['use_config']) && !empty($categoryPostData['use_config'])) { + foreach ($categoryPostData['use_config'] as $attributeCode => $attributeValue) { if ($attributeValue) { $useConfig[] = $attributeCode; $category->setData($attributeCode, null); @@ -141,11 +142,11 @@ public function execute() $category->setAttributeSetId($category->getDefaultAttributeSetId()); - if (isset($data['general']['category_products']) - && is_string($data['general']['category_products']) + if (isset($categoryPostData['category_products']) + && is_string($categoryPostData['category_products']) && !$category->getProductsReadonly() ) { - $products = json_decode($data['general']['category_products'], true); + $products = json_decode($categoryPostData['category_products'], true); $category->setPostedProducts($products); } $this->_eventManager->dispatch( @@ -156,8 +157,8 @@ public function execute() /** * Check "Use Default Value" checkboxes values */ - if (isset($generalPost['use_default']) && !empty($generalPost['use_default'])) { - foreach ($generalPost['use_default'] as $attributeCode => $attributeValue) { + if (isset($categoryPostData['use_default']) && !empty($categoryPostData['use_default'])) { + foreach ($categoryPostData['use_default'] as $attributeCode => $attributeValue) { if ($attributeValue) { $category->setData($attributeCode, null); } @@ -197,15 +198,15 @@ public function execute() } catch (\Magento\Framework\Exception\AlreadyExistsException $e) { $this->messageManager->addError($e->getMessage()); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); - $this->_getSession()->setCategoryData($data); + $this->_getSession()->setCategoryData($categoryPostData); } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->messageManager->addError($e->getMessage()); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); - $this->_getSession()->setCategoryData($data); + $this->_getSession()->setCategoryData($categoryPostData); } catch (\Exception $e) { $this->messageManager->addError(__('Something went wrong while saving the category.')); $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); - $this->_getSession()->setCategoryData($data); + $this->_getSession()->setCategoryData($categoryPostData); } } @@ -248,9 +249,9 @@ public function execute() */ public function imagePreprocessing($data) { - if (empty($data['general']['image'])) { - unset($data['general']['image']); - $data['general']['image']['delete'] = true; + if (empty($data['image'])) { + unset($data['image']); + $data['image']['delete'] = true; } return $data; } diff --git a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php index f2e2ea6c78816..6c114089789c6 100644 --- a/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php +++ b/app/code/Magento/Catalog/Model/Category/Attribute/Backend/Image.php @@ -111,6 +111,9 @@ public function afterSave($object) } catch (\Exception $e) { $this->_logger->critical($e); } + } elseif (isset($value[0]['name'])) { + $object->setData($this->getAttribute()->getName(), $value[0]['name']); + $this->getAttribute()->getEntity()->saveAttribute($object, $this->getAttribute()->getName()); } return $this; } diff --git a/app/code/Magento/Catalog/Model/Category/DataProvider.php b/app/code/Magento/Catalog/Model/Category/DataProvider.php index e1d1ee848f288..8626d183e2fac 100644 --- a/app/code/Magento/Catalog/Model/Category/DataProvider.php +++ b/app/code/Magento/Catalog/Model/Category/DataProvider.php @@ -201,9 +201,7 @@ public function getData() return $this->loadedData; } $category = $this->getCurrentCategory(); - if (!$category->getId()) { - return []; - } else { + if ($category) { $categoryData = $category->getData(); $categoryData = $this->addUseDefaultSettings($category, $categoryData); $categoryData = $this->addUseConfigSettings($categoryData); diff --git a/app/code/Magento/Catalog/Model/Product/Option.php b/app/code/Magento/Catalog/Model/Product/Option.php index 86ed6714529d8..2c9e09f8aff30 100644 --- a/app/code/Magento/Catalog/Model/Product/Option.php +++ b/app/code/Magento/Catalog/Model/Product/Option.php @@ -63,6 +63,11 @@ class Option extends AbstractExtensibleModel implements ProductCustomOptionInter */ protected $optionRepository; + /** + * Option type percent + */ + protected static $typePercent = 'percent'; + /**#@+ * Constants */ @@ -424,7 +429,7 @@ public function afterSave() */ public function getPrice($flag = false) { - if ($flag && $this->getPriceType() == 'percent') { + if ($flag && $this->getPriceType() == self::$typePercent) { $basePrice = $this->getProduct()->getPriceInfo()->getPrice(BasePrice::PRICE_CODE)->getValue(); $price = $basePrice * ($this->_getData(self::KEY_PRICE) / 100); return $price; @@ -827,6 +832,21 @@ public function getExtensionAttributes() return $this->_getExtensionAttributes(); } + /** + * Return regular price. + * + * @return float|int + */ + public function getRegularPrice() + { + if ($this->getPriceType() == self::$typePercent) { + $basePrice = $this->getProduct()->getPriceInfo()->getPrice('regular_price')->getAmount()->getValue(); + $price = $basePrice * ($this->_getData(self::KEY_PRICE) / 100); + return $price; + } + return $this->_getData(self::KEY_PRICE); + } + /** * @param Product $product * @return \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection diff --git a/app/code/Magento/Catalog/Setup/UpgradeData.php b/app/code/Magento/Catalog/Setup/UpgradeData.php index 542fea6770fab..ab32e8c5a6c7d 100644 --- a/app/code/Magento/Catalog/Setup/UpgradeData.php +++ b/app/code/Magento/Catalog/Setup/UpgradeData.php @@ -338,7 +338,20 @@ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface ] ); } + + if (version_compare($context->getVersion(), '2.0.7') < 0) { + /** @var EavSetup $eavSetupF */ + $eavSetup= $this->eavSetupFactory->create(['setup' => $setup]); + $eavSetup->updateAttribute( + ProductAttributeInterface::ENTITY_TYPE_CODE, + 'meta_description', + [ + 'note' => 'Maximum 255 chars. Meta Description should optimally be between 150-160 characters' + ] + ); + } + $setup->endSetup(); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/OptionTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/OptionTest.php index 8b938938c20e1..ea01f87a1ad6c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/OptionTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/OptionTest.php @@ -5,6 +5,7 @@ */ namespace Magento\Catalog\Test\Unit\Model\Product; +use \Magento\Catalog\Model\Product\Option; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; class OptionTest extends \PHPUnit_Framework_TestCase @@ -33,4 +34,29 @@ public function testGetProductSku() $this->productMock->expects($this->once())->method('getSku')->willReturn($productSku); $this->assertEquals($productSku, $this->model->getProductSku()); } + + public function testGetRegularPrice() + { + $priceInfoMock = $this->getMockForAbstractClass( + 'Magento\Framework\Pricing\PriceInfoInterface', + [], + '', + false, + false, + true, + ['getAmount', 'getPrice'] + ); + $priceInfoMock->expects($this->once())->method('getPrice')->willReturnSelf(); + $amountMock = $this->getMockForAbstractClass('Magento\Framework\Pricing\Amount\AmountInterface'); + $priceInfoMock->expects($this->once())->method('getAmount')->willReturn($amountMock); + + $this->productMock->expects($this->once())->method('getPriceInfo')->willReturn($priceInfoMock); + + $amountMock->expects($this->once())->method('getValue')->willReturn(50); + $this->model->setPrice(50); + $this->model->setPriceType(\Magento\Catalog\Model\Product\Option\Value::TYPE_PERCENT); + $this->assertEquals(25, $this->model->getRegularPrice()); + $this->model->setPriceType(null); + $this->assertEquals(50, $this->model->getRegularPrice()); + } } diff --git a/app/code/Magento/Catalog/etc/module.xml b/app/code/Magento/Catalog/etc/module.xml index ac6a681cd5ebd..87e82543fc65b 100644 --- a/app/code/Magento/Catalog/etc/module.xml +++ b/app/code/Magento/Catalog/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php index d2aac2a8369e9..87340140ada3f 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php @@ -724,7 +724,7 @@ protected function critical($e) private function roundTime($timeStamp) { if (is_numeric($timeStamp) && $timeStamp != 0) { - $timeStamp = $this->dateTime->timestamp($this->dateTime->date('Y-m-d 00:00:00')); + $timeStamp = $this->dateTime->timestamp($this->dateTime->date('Y-m-d 00:00:00', $timeStamp)); } return $timeStamp; diff --git a/app/code/Magento/Eav/Setup/EavSetup.php b/app/code/Magento/Eav/Setup/EavSetup.php index b585435dee2c5..a53ed60774749 100644 --- a/app/code/Magento/Eav/Setup/EavSetup.php +++ b/app/code/Magento/Eav/Setup/EavSetup.php @@ -909,7 +909,7 @@ public function addAttributeOption($option) * * @param int|string $entityTypeId * @param int|string $id - * @param string $field + * @param string|array $field * @param mixed $value * @param int $sortOrder * @return $this diff --git a/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php index 0be86320e6ca7..6dc61d9110a5f 100644 --- a/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php +++ b/app/code/Magento/Indexer/Console/Command/IndexerReindexCommand.php @@ -16,6 +16,16 @@ */ class IndexerReindexCommand extends AbstractIndexerManageCommand { + /** + * @var array + */ + private $sharedIndexesComplete = []; + + /** + * @var \Magento\Framework\Indexer\ConfigInterface + */ + private $config; + /** * {@inheritdoc} */ @@ -34,21 +44,19 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { $indexers = $this->getIndexers($input); - $config = $this->getConfig(); - $sharedIndexesComplete = []; foreach ($indexers as $indexer) { try { + $this->validateIndexerStatus($indexer); $startTime = microtime(true); - $indexerConfig = $config->getIndexer($indexer->getId()); + $indexerConfig = $this->getConfig()->getIndexer($indexer->getId()); + $sharedIndex = $indexerConfig['shared_index']; // Skip indexers having shared index that was already complete - if (!in_array($indexerConfig['shared_index'], $sharedIndexesComplete)) { + if (!in_array($sharedIndex, $this->sharedIndexesComplete)) { $indexer->reindexAll(); - } else { - $indexer->getState()->setStatus(StateInterface::STATUS_VALID)->save(); - } - if ($indexerConfig['shared_index']) { - $sharedIndexesComplete[] = $indexerConfig['shared_index']; + if ($sharedIndex) { + $this->validateSharedIndex($sharedIndex); + } } $resultTime = microtime(true) - $startTime; $output->writeln( @@ -63,6 +71,72 @@ protected function execute(InputInterface $input, OutputInterface $output) } } + /** + * Validate that indexer is not locked + * + * @param \Magento\Framework\Indexer\IndexerInterface $indexer + * @return void + * @throws LocalizedException + */ + private function validateIndexerStatus(\Magento\Framework\Indexer\IndexerInterface $indexer) + { + if ($indexer->getStatus() == \Magento\Framework\Indexer\StateInterface::STATUS_WORKING) { + throw new LocalizedException( + __( + '%1 index is locked by another reindex process. Skipping.', + $indexer->getTitle() + ) + ); + } + } + + /** + * Get indexer ids that have common shared index + * + * @param string $sharedIndex + * @return array + */ + private function getIndexerIdsBySharedIndex($sharedIndex) + { + $indexers = $this->getConfig()->getIndexers(); + $result = []; + foreach ($indexers as $indexerConfig) { + if ($indexerConfig['shared_index'] == $sharedIndex) { + $result[] = $indexerConfig['indexer_id']; + } + } + return $result; + } + + /** + * Validate indexers by shared index ID + * + * @param string $sharedIndex + * @return $this + */ + private function validateSharedIndex($sharedIndex) + { + if (empty($sharedIndex)) { + throw new \InvalidArgumentException('sharedIndex must be a valid shared index identifier'); + } + $indexerIds = $this->getIndexerIdsBySharedIndex($sharedIndex); + if (empty($indexerIds)) { + return $this; + } + $indexerFactory = $this->getObjectManager()->create('Magento\Indexer\Model\IndexerFactory'); + foreach ($indexerIds as $indexerId) { + /** @var \Magento\Indexer\Model\Indexer $indexer */ + $indexer = $indexerFactory->create(); + $indexer->load($indexerId); + /** @var \Magento\Indexer\Model\Indexer\State $state */ + $state = $indexer->getState(); + $state->setStatus(\Magento\Framework\Indexer\StateInterface::STATUS_VALID); + $state->save(); + } + $this->sharedIndexesComplete[] = $sharedIndex; + return $this; + } + /** * Get config * @@ -71,6 +145,9 @@ protected function execute(InputInterface $input, OutputInterface $output) */ private function getConfig() { - return $this->getObjectManager()->get(ConfigInterface::class); + if (!$this->config) { + $this->config = $this->getObjectManager()->get(ConfigInterface::class); + } + return $this->config; } } diff --git a/app/code/Magento/Indexer/Console/Command/IndexerResetStateCommand.php b/app/code/Magento/Indexer/Console/Command/IndexerResetStateCommand.php new file mode 100644 index 0000000000000..3b1e952d1e619 --- /dev/null +++ b/app/code/Magento/Indexer/Console/Command/IndexerResetStateCommand.php @@ -0,0 +1,51 @@ +setName('indexer:reset') + ->setDescription('Resets indexer status to invalid') + ->setDefinition($this->getInputList()); + + parent::configure(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $indexers = $this->getIndexers($input); + foreach ($indexers as $indexer) { + try { + $indexer->getState() + ->setStatus(\Magento\Framework\Indexer\StateInterface::STATUS_INVALID) + ->save(); + $output->writeln($indexer->getTitle() . ' indexer has been invalidated.'); + } catch (LocalizedException $e) { + $output->writeln($e->getMessage()); + } catch (\Exception $e) { + $output->writeln($indexer->getTitle() . ' indexer process unknown error:'); + $output->writeln($e->getMessage()); + } + } + } +} diff --git a/app/code/Magento/Indexer/README.md b/app/code/Magento/Indexer/README.md index fe6d85ba0fcab..b0a88d12ad0ed 100644 --- a/app/code/Magento/Indexer/README.md +++ b/app/code/Magento/Indexer/README.md @@ -1,4 +1,12 @@ +## Overview Magento_Indexer module is a base of Magento Indexing functionality. -It allows to read indexers configuration, represent indexers in admin, control their mode, regenerate indexers by schedule. +It allows: + - read indexers configuration, + - represent indexers in admin, + - regenerate indexes by cron schedule, + - regenerate indexes from console, + - view and reset indexer state from console, + - view and set indexer mode from console + There are 2 modes of the Indexers: "Update on save" and "Update by schedule". -Manual re-index can be performed via console by running index.php script. +Manual full reindex can be performed via console by running `php -f bin/magento indexer:reindex` console command. \ No newline at end of file diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerReindexCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerReindexCommandTest.php index f6e8b6622e4a9..40923b6418ce4 100644 --- a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerReindexCommandTest.php +++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerReindexCommandTest.php @@ -86,36 +86,60 @@ public function testExecuteWithIndex() ['id_indexer3', ['title' => 'Title_indexer3', 'shared_index' => 'with_indexer_3']] ] )); + $this->configMock->expects($this->any()) + ->method('getIndexers') + ->will($this->returnValue( + [ + 'id_indexerOne' => [ + 'indexer_id' => 'id_indexerOne', + 'title' => 'Title_indexerOne', + 'shared_index' => null + ], + 'id_indexerTwo' => [ + 'indexer_id' => 'id_indexerTwo', + 'title' => 'Title_indexerTwo', + 'shared_index' => 'with_indexer_3' + ], + 'id_indexer3' => [ + 'indexer_id' => 'id_indexer3', + 'title' => 'Title_indexer3', + 'shared_index' => 'with_indexer_3' + ] + ] + )); $this->configureAdminArea(); $indexerOne = $this->getMock('Magento\Indexer\Model\Indexer', [], [], '', false); $indexerOne->expects($this->once())->method('reindexAll'); $indexerOne->expects($this->once())->method('getTitle')->willReturn('Title_indexerOne'); $indexerOne->expects($this->any())->method('getId')->willReturn('id_indexerOne'); - $indexerOne->expects($this->once())->method('load')->with('id_indexerOne')->willReturn($indexerOne); + $indexerOne->expects($this->any())->method('load')->with('id_indexerOne')->willReturn($indexerOne); $indexerTwo = $this->getMock('Magento\Indexer\Model\Indexer', [], [], '', false); $indexerTwo->expects($this->once())->method('reindexAll'); $indexerTwo->expects($this->once())->method('getTitle')->willReturn('Title_indexerTwo'); $indexerTwo->expects($this->any())->method('getId')->willReturn('id_indexerTwo'); - $indexerTwo->expects($this->once())->method('load')->with('id_indexerTwo')->willReturn($indexerTwo); + $indexerTwo->expects($this->any())->method('load')->with('id_indexerTwo')->willReturn($indexerTwo); $indexer3 = $this->getMock('Magento\Indexer\Model\Indexer', [], [], '', false); $indexer3->expects($this->never())->method('reindexAll'); $indexer3->expects($this->once())->method('getTitle')->willReturn('Title_indexer3'); $indexer3->expects($this->any())->method('getId')->willReturn('id_indexer3'); - $indexer3->expects($this->once())->method('load')->with('id_indexer3')->willReturn($indexer3); + $indexer3->expects($this->any())->method('load')->with('id_indexer3')->willReturn($indexer3); $stateMock = $this->getMock(\Magento\Indexer\Model\Indexer\State::class, [], [], '', false); - $stateMock->expects($this->once())->method('setStatus')->will($this->returnSelf()); - $stateMock->expects($this->once())->method('save'); + $stateMock->expects($this->exactly(2))->method('setStatus')->will($this->returnSelf()); + $stateMock->expects($this->exactly(2))->method('save'); $indexer3->expects($this->once())->method('getState')->willReturn($stateMock); + $indexerTwo->expects($this->once())->method('getState')->willReturn($stateMock); $this->collectionFactory->expects($this->never())->method('create'); $this->indexerFactory->expects($this->at(0))->method('create')->willReturn($indexerOne); $this->indexerFactory->expects($this->at(1))->method('create')->willReturn($indexerTwo); $this->indexerFactory->expects($this->at(2))->method('create')->willReturn($indexer3); + $this->indexerFactory->expects($this->at(3))->method('create')->willReturn($indexerTwo); + $this->indexerFactory->expects($this->at(4))->method('create')->willReturn($indexer3); $this->command = new IndexerReindexCommand($this->objectManagerFactory); $commandTester = new CommandTester($this->command); diff --git a/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerResetStateCommandTest.php b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerResetStateCommandTest.php new file mode 100644 index 0000000000000..5dac864ab277b --- /dev/null +++ b/app/code/Magento/Indexer/Test/Unit/Console/Command/IndexerResetStateCommandTest.php @@ -0,0 +1,64 @@ +stateMock->expects($this->once())->method('setAreaCode')->with(FrontNameResolver::AREA_CODE); + } + + public function testExecute() + { + $this->configureAdminArea(); + $collection = $this->getMock('Magento\Indexer\Model\Indexer\Collection', [], [], '', false); + $indexerOne = $this->getMock('Magento\Indexer\Model\Indexer', [], [], '', false); + + $indexerOne->expects($this->once()) + ->method('getTitle') + ->willReturn('Title_indexerOne'); + + $collection->expects($this->once()) + ->method('getItems') + ->willReturn([$indexerOne]); + + $stateMock = $this->getMock(\Magento\Indexer\Model\Indexer\State::class, [], [], '', false); + $stateMock->expects($this->exactly(1)) + ->method('setStatus') + ->with(\Magento\Framework\Indexer\StateInterface::STATUS_INVALID) + ->will($this->returnSelf()); + + $stateMock->expects($this->exactly(1)) + ->method('save'); + + $indexerOne->expects($this->once()) + ->method('getState') + ->willReturn($stateMock); + + $this->collectionFactory->expects($this->once()) + ->method('create') + ->will($this->returnValue($collection)); + + $this->command = new IndexerResetStateCommand($this->objectManagerFactory); + $commandTester = new CommandTester($this->command); + $commandTester->execute([]); + $actualValue = $commandTester->getDisplay(); + $this->assertSame(sprintf('Title_indexerOne indexer has been invalidated.') . PHP_EOL, $actualValue); + } +} diff --git a/app/code/Magento/Indexer/etc/di.xml b/app/code/Magento/Indexer/etc/di.xml index 019dad475a553..b29cc6ca71d1a 100644 --- a/app/code/Magento/Indexer/etc/di.xml +++ b/app/code/Magento/Indexer/etc/di.xml @@ -51,6 +51,7 @@ Magento\Indexer\Console\Command\IndexerSetModeCommand Magento\Indexer\Console\Command\IndexerShowModeCommand Magento\Indexer\Console\Command\IndexerStatusCommand + Magento\Indexer\Console\Command\IndexerResetStateCommand diff --git a/app/code/Magento/Reports/Model/ResourceModel/Review/Product/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Review/Product/Collection.php index ccfbc983aab67..dae41f6d59a4f 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Review/Product/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Review/Product/Collection.php @@ -47,7 +47,7 @@ protected function _joinReview() 'e.entity_id = r.entity_pk_value', [ 'review_cnt' => new \Zend_Db_Expr(sprintf('(%s)', $subSelect)), - 'last_created' => 'MAX(r.created_at)' + 'created_at' => 'MAX(r.created_at)' ] )->group( 'e.entity_id' @@ -83,7 +83,7 @@ protected function _joinReview() */ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC) { - if (in_array($attribute, ['review_cnt', 'last_created', 'avg_rating', 'avg_rating_approved'])) { + if (in_array($attribute, ['review_cnt', 'created_at', 'avg_rating', 'avg_rating_approved'])) { $this->getSelect()->order($attribute . ' ' . $dir); return $this; } diff --git a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_product_grid.xml b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_product_grid.xml index 094740686678f..c795a9e29223c 100644 --- a/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_product_grid.xml +++ b/app/code/Magento/Reports/view/adminhtml/layout/reports_report_review_product_grid.xml @@ -86,12 +86,12 @@ col-avg-rating - + Last Review datetime - last_created - last_created + created_at + created_at col-date col-date diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Edit/CategoryForm.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Edit/CategoryForm.xml index 03c5cd3c02995..593ab70e100ff 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Edit/CategoryForm.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Edit/CategoryForm.xml @@ -110,6 +110,10 @@ checkbox input[name='custom_use_parent_settings'] + + select + select[name='custom_design'] + select select[name='page_layout'] @@ -137,10 +141,6 @@ text input[name='custom_design_to'] - - select - select[name='custom_design'] - diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/PromotedSection.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/PromotedSection.php index db0cab463a417..0f7c04da94bdf 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/PromotedSection.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/PromotedSection.php @@ -62,6 +62,10 @@ public function getProductItem(FixtureInterface $product) */ public function getProducts() { + if (!$this->_rootElement->isVisible($this->productItem)) { + return []; + } + $elements = $this->_rootElement->getElements($this->productItem, Locator::SELECTOR_CSS); $result = []; diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/Related.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/Related.php index f1b1e029f62d4..c81cab9de5297 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/Related.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/Related.php @@ -38,6 +38,10 @@ public function getProductItem(FixtureInterface $product) */ public function getProducts() { + if (!$this->_rootElement->isVisible($this->productItem)) { + return []; + } + $elements = $this->_rootElement->getElements($this->productItem, Locator::SELECTOR_CSS); $result = []; diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryBreadcrumbs.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryBreadcrumbs.php index b5102db734e42..c1ed4c8bbd182 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryBreadcrumbs.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryBreadcrumbs.php @@ -45,6 +45,10 @@ public function processAssert( $this->openCategory($category); $breadcrumbs = $this->getBreadcrumbs($category); + \PHPUnit_Framework_Assert::assertNotEmpty( + $breadcrumbs, + 'No breadcrumbs on category \''. $category->getName() . '\' page.' + ); $pageBreadcrumbs = $catalogCategoryView->getBreadcrumbs()->getText(); \PHPUnit_Framework_Assert::assertEquals( $breadcrumbs, diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryForAssignedProducts.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryForAssignedProducts.php index 936d00040de48..25166e66073f4 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryForAssignedProducts.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertCategoryForAssignedProducts.php @@ -40,7 +40,7 @@ public function processAssert( foreach ($products as $productFixture) { \PHPUnit_Framework_Assert::assertTrue( $categoryView->getListProductBlock()->getProductItem($productFixture)->isVisible(), - "Products '{$productFixture->getName()}' not found." + "Products '{$productFixture->getName()}' not found in category '{$category->getName()}'." ); } } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductCrossSells.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductCrossSells.php index 7cad535e5e02c..b053218966634 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductCrossSells.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductCrossSells.php @@ -51,7 +51,8 @@ public function processAssert( $checkoutCart->open(); foreach ($promotedProducts as $promotedProduct) { if (!$checkoutCart->getCrosssellBlock()->getProductItem($promotedProduct)->isVisible()) { - $errors[] = 'Product \'' . $promotedProduct->getName() . '\' is absent in cross-sell section.'; + $errors[] = 'Product \'' . $promotedProduct->getName() + . '\' is absent in cross-sell section of a product \'' . $product->getName() . '\'.'; } } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductRelatedProducts.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductRelatedProducts.php index bc1a6cfbc35f4..cb3db6bd63488 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductRelatedProducts.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductRelatedProducts.php @@ -46,7 +46,8 @@ public function processAssert( foreach ($promotedProducts as $promotedProduct) { \PHPUnit_Framework_Assert::assertTrue( $catalogProductView->getRelatedProductBlock()->getProductItem($promotedProduct)->isVisible(), - 'Product \'' . $promotedProduct->getName() . '\' is absent in related products.' + 'Product \'' . $promotedProduct->getName() + . '\' is absent in related products \'' . $product->getName() . '\'.' ); } } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductUpSells.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductUpSells.php index e85edcdbd081b..f35a3004fda6d 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductUpSells.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductUpSells.php @@ -46,7 +46,8 @@ public function processAssert( foreach ($promotedProducts as $promotedProduct) { \PHPUnit_Framework_Assert::assertTrue( $catalogProductView->getUpsellBlock()->getProductItem($promotedProduct)->isVisible(), - 'Product \'' . $promotedProduct->getName() . '\' is absent in up-sells products.' + 'Product \'' . $promotedProduct->getName() + . '\' is absent in up-sells products of a product \'' . $product->getName() . '\'.' ); } } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category.xml index eb4967eec2616..b9ba495535e82 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Fixture/Category.xml @@ -46,11 +46,11 @@ + - diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple.xml index 6ca96f063ea9b..b79d1b61709dd 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple.xml @@ -227,7 +227,7 @@ product_100_dollar%isolation% product_100_dollar%isolation% - 666.0000 + 777.0000 In Stock This item has weight @@ -252,7 +252,7 @@ product_40_dollar%isolation% simple - 666 + 777 In Stock This item has weight @@ -277,7 +277,7 @@ default - 666 + 777 In Stock This item has weight @@ -300,7 +300,7 @@ simple_product_with_category_%isolation% Simple product with category %isolation% - 666 + 777 In Stock This item has weight @@ -349,7 +349,7 @@ default - 666 + 777 In Stock Simple Product %isolation% @@ -487,7 +487,7 @@ None - 666 + 777 In Stock This item has weight @@ -511,7 +511,7 @@ None - 666 + 777 In Stock This item has weight @@ -535,7 +535,7 @@ None - 666 + 777 In Stock This item has weight @@ -559,7 +559,7 @@ taxable_goods - 666 + 777 In Stock This item has weight @@ -973,7 +973,7 @@ simple_product_with_category_%isolation% Simple product with category %isolation% - 666 + 777 In Stock This item has weight diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductVirtual.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductVirtual.xml index 164053ada9525..3fb2ff43914fa 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductVirtual.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductVirtual.xml @@ -23,7 +23,7 @@ Virtual product %isolation% sku_virtual_product_%isolation% - 666 + 777 In Stock This item has no weight @@ -112,7 +112,7 @@ Virtual product %isolation% sku_virtual_product_%isolation% - 666 + 777 In Stock diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml index e69adac6123d9..7e6f913dda6e7 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml @@ -36,11 +36,11 @@ custom meta keywords %isolation% Custom meta description %isolation% 2 columns with right bar - LAYOUT UPDATE XML - Yes - 01/10/2014 - 12/31/2024 + <referenceContainer name="catalog.leftnav" remove="true"/> Magento Luma + Yes + Jan 10, 2014 + Dec 31, 2024 @@ -79,11 +79,11 @@ Custom meta description %isolation% catalogProductSimple::default,catalogProductSimple::default 2 columns with right bar - LAYOUT UPDATE XML - Yes - 01/10/2014 - 12/31/2024 + <referenceContainer name="content.aside" remove="true"/> Magento Luma + Yes + Jan 1, 2014 + Dec 31, 2024