diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php index d402c81becf1ce21e636b6d36082a47ddcc2f155..d59492c4065e72706f8c1a168e4a33b1c2c9e825 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper.php @@ -199,6 +199,9 @@ class Helper $customOptions = []; foreach ($options as $customOptionData) { if (empty($customOptionData['is_delete'])) { + if (empty($customOptionData['option_id'])) { + $customOptionData['option_id'] = null; + } if (isset($customOptionData['values'])) { $customOptionData['values'] = array_filter($customOptionData['values'], function ($valueData) { return empty($valueData['is_delete']); @@ -206,7 +209,6 @@ class Helper } $customOption = $this->getCustomOptionFactory()->create(['data' => $customOptionData]); $customOption->setProductSku($product->getSku()); - $customOption->setOptionId(null); $customOptions[] = $customOption; } } @@ -255,7 +257,7 @@ class Helper foreach ($linkTypes as $linkType => $readonly) { if (isset($links[$linkType]) && !$readonly) { - foreach ((array) $links[$linkType] as $linkData) { + foreach ((array)$links[$linkType] as $linkData) { if (empty($linkData['id'])) { continue; } @@ -321,9 +323,11 @@ class Helper if (isset($option['values']) && isset($overwriteOptions[$optionId]['values'])) { foreach ($option['values'] as $valueIndex => $value) { - $valueId = $value['option_type_id']; - $value = $this->overwriteValue($valueId, $value, $overwriteOptions[$optionId]['values']); - $option['values'][$valueIndex] = $value; + if (isset($value['option_type_id'])) { + $valueId = $value['option_type_id']; + $value = $this->overwriteValue($valueId, $value, $overwriteOptions[$optionId]['values']); + $option['values'][$valueIndex] = $value; + } } } @@ -347,6 +351,9 @@ class Helper foreach ($overwriteOptions[$optionId] as $fieldName => $overwrite) { if ($overwrite && isset($option[$fieldName]) && isset($option['default_' . $fieldName])) { $option[$fieldName] = $option['default_' . $fieldName]; + if ('title' == $fieldName) { + $option['is_delete_store_title'] = 1; + } } } } diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php index 0036b3b2ab54a5a2f4ff104dbe7b8a3da4526d28..4375092591d194962b108e2ba0351b1a03fa7e5c 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/FlatTableBuilder.php @@ -221,7 +221,11 @@ class FlatTableBuilder unset($tables[$entityTableName]); - $allColumns = array_merge(['entity_id', 'type_id', 'attribute_set_id'], $columnsList); + $allColumns = array_values( + array_unique( + array_merge(['entity_id', $linkField, 'type_id', 'attribute_set_id'], $columnsList) + ) + ); /* @var $status \Magento\Eav\Model\Entity\Attribute */ $status = $this->_productIndexerHelper->getAttribute('status'); @@ -263,7 +267,7 @@ class FlatTableBuilder $select->joinLeft( $temporaryTableName, - "e.entity_id = " . $temporaryTableName . ".entity_id", + sprintf('e.%1$s = %2$s.%1$s', $linkField, $temporaryTableName), $columnsNames ); $allColumns = array_merge($allColumns, $columnsNames); @@ -277,7 +281,7 @@ class FlatTableBuilder if (!empty($columnValueNames)) { $select->joinLeft( $temporaryValueTableName, - "e.${linkField} = " . $temporaryValueTableName . ".entity_id", + sprintf('e.%1$s = %2$s.%1$s', $linkField, $temporaryTableName), $columnValueNames ); $allColumns = array_merge($allColumns, $columnValueNames); diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Table/Builder.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Table/Builder.php new file mode 100644 index 0000000000000000000000000000000000000000..192f411c93b4ea48c1b753d88f586a25aca41838 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Table/Builder.php @@ -0,0 +1,45 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Model\Indexer\Product\Flat\Table; + +/** + * Class Builder + */ +class Builder implements BuilderInterface +{ + /** + * @var \Magento\Framework\DB\Ddl\Table + */ + private $tableInstance; + + /** + * Builder constructor. + * + * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection + * @param string $tableName + */ + public function __construct(\Magento\Framework\DB\Adapter\AdapterInterface $connection, $tableName) + { + $this->tableInstance = $connection->newTable($tableName); + } + + /** + * @inheritdoc + */ + public function addColumn($name, $type, $size = null, $options = [], $comment = null) + { + $this->tableInstance->addColumn($name, $type, $size, $options, $comment); + return $this; + } + + /** + * @inheritdoc + */ + public function getTable() + { + return $this->tableInstance; + } +} diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Table/BuilderInterface.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Table/BuilderInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..b24db9ee74334b743988b4a964004c41a5c610c7 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/Table/BuilderInterface.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Model\Indexer\Product\Flat\Table; + +/** + * Interface BuilderInterface + */ +interface BuilderInterface +{ + /** + * Adds column to table. + * + * $options contains additional options for columns. Supported values are: + * - 'unsigned', for number types only. Default: FALSE. + * - 'precision', for numeric and decimal only. Default: taken from $size, if not set there then 0. + * - 'scale', for numeric and decimal only. Default: taken from $size, if not set there then 10. + * - 'default'. Default: not set. + * - 'nullable'. Default: TRUE. + * - 'primary', add column to primary index. Default: do not add. + * - 'primary_position', only for column in primary index. Default: count of primary columns + 1. + * - 'identity' or 'auto_increment'. Default: FALSE. + * + * @param string $name the column name + * @param string $type the column data type + * @param string|int|array $size the column length + * @param array $options array of additional options + * @param string $comment column description + * @return $this + * @throws \Zend_Db_Exception + */ + public function addColumn($name, $type, $size = null, $options = [], $comment = null); + + /** + * @return \Magento\Framework\DB\Ddl\Table + */ + public function getTable(); +} diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/TableBuilder.php b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/TableBuilder.php index fdc405b27d3fd8b8b41c69395397c38235c8412e..d00a720a91ddefdad1036c52a0c12d6c5f10d7b3 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Flat/TableBuilder.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Flat/TableBuilder.php @@ -5,7 +5,7 @@ */ namespace Magento\Catalog\Model\Indexer\Product\Flat; -use Magento\Framework\App\ResourceConnection; +use Magento\Catalog\Model\Indexer\Product\Flat\Table\BuilderInterfaceFactory; /** * Class TableBuilder @@ -32,6 +32,11 @@ class TableBuilder */ protected $resource; + /** + * @var BuilderInterfaceFactory + */ + private $tableBuilderFactory; + /** * Check whether builder was executed * @@ -123,17 +128,27 @@ class TableBuilder $valueTables = []; if (!empty($columns)) { $valueTableName = $tableName . $valueFieldSuffix; - $temporaryTable = $this->_connection->newTable($tableName); - $valueTemporaryTable = $this->_connection->newTable($valueTableName); + $temporaryTableBuilder = $this->getTableBuilderFactory()->create( + [ + 'connection' => $this->_connection, + 'tableName' => $tableName + ] + ); + $valueTemporaryTableBuilder = $this->getTableBuilderFactory()->create( + [ + 'connection' => $this->_connection, + 'tableName' => $valueTableName + ] + ); $flatColumns = $this->_productIndexerHelper->getFlatColumns(); - $temporaryTable->addColumn('entity_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER); + $temporaryTableBuilder->addColumn('entity_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER); - $temporaryTable->addColumn('type_id', \Magento\Framework\DB\Ddl\Table::TYPE_TEXT); + $temporaryTableBuilder->addColumn('type_id', \Magento\Framework\DB\Ddl\Table::TYPE_TEXT); - $temporaryTable->addColumn('attribute_set_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER); + $temporaryTableBuilder->addColumn('attribute_set_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER); - $valueTemporaryTable->addColumn('entity_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER); + $valueTemporaryTableBuilder->addColumn('entity_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER); /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ foreach ($columns as $columnName => $attribute) { @@ -145,7 +160,7 @@ class TableBuilder $column = $column[$attributeCode]; } - $temporaryTable->addColumn( + $temporaryTableBuilder->addColumn( $columnName, $column['type'], isset($column['length']) ? $column['length'] : null @@ -154,7 +169,7 @@ class TableBuilder $columnValueName = $attributeCode . $valueFieldSuffix; if (isset($flatColumns[$columnValueName])) { $columnValue = $flatColumns[$columnValueName]; - $valueTemporaryTable->addColumn( + $valueTemporaryTableBuilder->addColumn( $columnValueName, $columnValue['type'], isset($columnValue['length']) ? $columnValue['length'] : null @@ -162,11 +177,11 @@ class TableBuilder } } $this->_connection->dropTemporaryTable($tableName); - $this->_connection->createTemporaryTable($temporaryTable); + $this->_connection->createTemporaryTable($temporaryTableBuilder->getTable()); - if (count($valueTemporaryTable->getColumns()) > 1) { + if (count($valueTemporaryTableBuilder->getTable()->getColumns()) > 1) { $this->_connection->dropTemporaryTable($valueTableName); - $this->_connection->createTemporaryTable($valueTemporaryTable); + $this->_connection->createTemporaryTable($valueTemporaryTableBuilder->getTable()); $valueTables[$valueTableName] = $valueTableName; } } @@ -197,7 +212,8 @@ class TableBuilder if (!empty($columns)) { $select = $this->_connection->select(); $temporaryEntityTable = $this->_getTemporaryTableName($tableName); - $idsColumns = ['entity_id', 'type_id', 'attribute_set_id']; + $metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class); + $idsColumns = array_unique([$metadata->getLinkField(), 'entity_id', 'type_id', 'attribute_set_id']); $columns = array_merge($idsColumns, array_keys($columns)); @@ -261,7 +277,7 @@ class TableBuilder ); $temporaryTableName = $this->_getTemporaryTableName($tableName); $temporaryValueTableName = $temporaryTableName . $valueFieldSuffix; - $keyColumn = ['entity_id']; + $keyColumn = array_unique([$metadata->getLinkField(), 'entity_id']); $columns = array_merge($keyColumn, array_keys($columnsList)); $valueColumns = $keyColumn; $flatColumns = $this->_productIndexerHelper->getFlatColumns(); @@ -333,6 +349,19 @@ class TableBuilder } } + /** + * @return BuilderInterfaceFactory + */ + private function getTableBuilderFactory() + { + if (null === $this->tableBuilderFactory) { + $this->tableBuilderFactory = \Magento\Framework\App\ObjectManager::getInstance() + ->get(BuilderInterfaceFactory::class); + } + + return $this->tableBuilderFactory; + } + /** * @return \Magento\Framework\EntityManager\MetadataPool */ diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php index ada156f571b0e60c8ff1a0ada71b308f9b62b38f..f17760237034041764539e533d5f1440b8dad3e4 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php @@ -160,12 +160,13 @@ class CreateHandler implements ExtensionInterface if (in_array($attrData, array_keys($existImages))) { $product->setData($mediaAttrCode . '_label', $existImages[$attrData]['label']); } - - $product->addAttributeUpdate( - $mediaAttrCode, - $product->getData($mediaAttrCode), - $product->getStoreId() - ); + if (!empty($product->getData($mediaAttrCode))) { + $product->addAttributeUpdate( + $mediaAttrCode, + $product->getData($mediaAttrCode), + $product->getStoreId() + ); + } } $product->setData($attrCode, $value); diff --git a/app/code/Magento/Catalog/Model/Product/Option/Repository.php b/app/code/Magento/Catalog/Model/Product/Option/Repository.php index d48bed8e3ccd08718e02c57d764e89c147ebf161..946bebd93c94029907b0ac7ed13dc6c1610b376c 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Repository.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Repository.php @@ -137,10 +137,33 @@ class Repository implements \Magento\Catalog\Api\ProductCustomOptionRepositoryIn if (!$productSku) { throw new CouldNotSaveException(__('ProductSku should be specified')); } + /** @var \Magento\Catalog\Model\Product $product */ $product = $this->productRepository->get($productSku); $metadata = $this->getMetadataPool()->getMetadata(ProductInterface::class); $option->setData('product_id', $product->getData($metadata->getLinkField())); - $option->setOptionId(null); + $option->setData('store_id', $product->getStoreId()); + + if ($option->getOptionId()) { + $options = $product->getOptions(); + if (!$options) { + $options = $this->getProductOptions($product); + } + + $persistedOption = array_filter($options, function ($iOption) use ($option) { + return $option->getOptionId() == $iOption->getOptionId(); + }); + $persistedOption = reset($persistedOption); + + if (!$persistedOption) { + throw new NoSuchEntityException(); + } + $originalValues = $persistedOption->getValues(); + $newValues = $option->getData('values'); + if ($newValues) { + $newValues = $this->markRemovedValues($newValues, $originalValues); + $option->setData('values', $newValues); + } + } $option->save(); return $option; } diff --git a/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php b/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php index 7c32232b6591aeb9a99a9b63f0b40300cd11dfe4..1c8c5b018ddf3b19642242431205756795fce45f 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Option/SaveHandler.php @@ -20,7 +20,6 @@ class SaveHandler implements ExtensionInterface /** * @param OptionRepository $optionRepository - * @param MetadataPool $metadataPool */ public function __construct( OptionRepository $optionRepository @@ -36,15 +35,28 @@ class SaveHandler implements ExtensionInterface */ public function execute($entity, $arguments = []) { + $options = $entity->getOptions(); + $optionIds = []; + + if ($options) { + $optionIds = array_map(function ($option) { + /** @var \Magento\Catalog\Model\Product\Option $option */ + return $option->getOptionId(); + }, $options); + } + /** @var \Magento\Catalog\Api\Data\ProductInterface $entity */ foreach ($this->optionRepository->getProductOptions($entity) as $option) { - $this->optionRepository->delete($option); + if (!in_array($option->getOptionId(), $optionIds)) { + $this->optionRepository->delete($option); + } } - if ($entity->getOptions()) { - foreach ($entity->getOptions() as $option) { + if ($options) { + foreach ($options as $option) { $this->optionRepository->save($option); } } + return $entity; } } 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 ba7103a5eada1acac077ae3ce432e0187e1fbe22..d1a317be7c7e0c2d09cd0b5bbddf6ab3844c96d6 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Validator/Select.php @@ -51,6 +51,9 @@ class Select extends DefaultValidator $storeId = $option->getProduct()->getStoreId(); } foreach ($values as $value) { + if (isset($value['is_delete']) && (bool)$value['is_delete']) { + continue; + } $type = isset($value['price_type']) ? $value['price_type'] : null; $price = isset($value['price']) ? $value['price'] : null; $title = isset($value['title']) ? $value['title'] : null; diff --git a/app/code/Magento/Catalog/Model/Product/Option/Value.php b/app/code/Magento/Catalog/Model/Product/Option/Value.php index 55a14e26333547543a14e22b7dcbc0342442596d..55c7c74e9a8e62bf14233f8c8b9b90309adfee66 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Value.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Value.php @@ -200,7 +200,7 @@ class Value extends AbstractModel implements \Magento\Catalog\Api\Data\ProductCu 'store_id', $this->getOption()->getStoreId() ); - $this->unsetData('option_type_id'); + if ($this->getData('is_delete') == '1') { if ($this->getId()) { $this->deleteValues($this->getId()); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php index 7b23e5979c8b841d9468f4c93596d4138c24d692..8dcdf441d7b85f014a5b6aa0398aa21592b81932 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php @@ -247,14 +247,21 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb $titleTableName = $this->getTable('catalog_product_option_title'); foreach ([\Magento\Store\Model\Store::DEFAULT_STORE_ID, $object->getStoreId()] as $storeId) { $existInCurrentStore = $this->getColFromOptionTable($titleTableName, (int)$object->getId(), (int)$storeId); - $existInDefaultStore = $this->getColFromOptionTable( - $titleTableName, - (int)$object->getId(), - \Magento\Store\Model\Store::DEFAULT_STORE_ID - ); + $existInDefaultStore = (int)$storeId == \Magento\Store\Model\Store::DEFAULT_STORE_ID ? + $existInCurrentStore : + $this->getColFromOptionTable( + $titleTableName, + (int)$object->getId(), + \Magento\Store\Model\Store::DEFAULT_STORE_ID + ); + if ($object->getTitle()) { + $isDeleteStoreTitle = (bool)$object->getData('is_delete_store_title'); if ($existInCurrentStore) { - if ($object->getStoreId() == $storeId) { + if ($isDeleteStoreTitle && (int)$storeId != \Magento\Store\Model\Store::DEFAULT_STORE_ID) { + $connection->delete($titleTableName, ['option_title_id = ?' => $existInCurrentStore]); + + } elseif ($object->getStoreId() == $storeId) { $data = $this->_prepareDataForTable( new \Magento\Framework\DataObject(['title' => $object->getTitle()]), $titleTableName @@ -270,8 +277,13 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb } } else { // we should insert record into not default store only of if it does not exist in default store - if (($storeId == \Magento\Store\Model\Store::DEFAULT_STORE_ID && !$existInDefaultStore) - || ($storeId != \Magento\Store\Model\Store::DEFAULT_STORE_ID && !$existInCurrentStore) + if ( + ($storeId == \Magento\Store\Model\Store::DEFAULT_STORE_ID && !$existInDefaultStore) || + ( + $storeId != \Magento\Store\Model\Store::DEFAULT_STORE_ID && + !$existInCurrentStore && + !$isDeleteStoreTitle + ) ) { $data = $this->_prepareDataForTable( new \Magento\Framework\DataObject( diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php index 42456a396178c1a1001c45f49a0150612cdd566f..4f3d65800d9457194f4b26817353cc1e281c0dd7 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option/Value.php @@ -237,6 +237,11 @@ class Value extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb ); $optionTypeId = $this->getConnection()->fetchOne($select); $existInCurrentStore = $this->getOptionIdFromOptionTable($titleTable, (int)$object->getId(), (int)$storeId); + + if ($storeId != \Magento\Store\Model\Store::DEFAULT_STORE_ID && $object->getData('is_delete_store_title')) { + $object->unsetData('title'); + } + if ($object->getTitle()) { if ($existInCurrentStore) { if ($storeId == $object->getStoreId()) { diff --git a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php index 389599f510f08961211d7cc212b757db9620547a..edd654a7393126c61b877d35b36de9077316ec4d 100644 --- a/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Initialization/HelperTest.php @@ -6,10 +6,11 @@ namespace Magento\Catalog\Test\Unit\Controller\Adminhtml\Product\Initialization; use Magento\Catalog\Api\Data\ProductLinkInterfaceFactory; -use Magento\Catalog\Api\ProductRepositoryInterface\Proxy as ProductRepository; use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper; use Magento\Catalog\Controller\Adminhtml\Product\Initialization\StockDataFilter; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Option; +use Magento\Catalog\Model\ProductRepository; use Magento\Framework\App\RequestInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Store\Api\Data\StoreInterface; @@ -17,7 +18,6 @@ use Magento\Store\Api\Data\WebsiteInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\Stdlib\DateTime\Filter\Date as DateFilter; use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory; -use Magento\Catalog\Api\Data\ProductCustomOptionInterface; use Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks; /** @@ -95,7 +95,7 @@ class HelperTest extends \PHPUnit_Framework_TestCase protected $customOptionFactoryMock; /** - * @var ProductCustomOptionInterface|\PHPUnit_Framework_MockObject_MockObject + * @var Option|\PHPUnit_Framework_MockObject_MockObject */ protected $customOptionMock; @@ -136,8 +136,6 @@ class HelperTest extends \PHPUnit_Framework_TestCase ->getMock(); $this->productMock = $this->getMockBuilder(Product::class) ->setMethods([ - 'setData', - 'addData', 'getId', 'setWebsiteIds', 'isLockedAttribute', @@ -145,7 +143,6 @@ class HelperTest extends \PHPUnit_Framework_TestCase 'getAttributes', 'unlockAttribute', 'getOptionsReadOnly', - 'setOptions', 'setCanSaveCustomOptions', '__sleep', '__wakeup', @@ -159,9 +156,10 @@ class HelperTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->setMethods(['create']) ->getMock(); - $this->customOptionMock = $this->getMockBuilder(ProductCustomOptionInterface::class) + $this->customOptionMock = $this->getMockBuilder(Option::class) ->disableOriginalConstructor() - ->getMockForAbstractClass(); + ->setMethods(null) + ->getMock(); $this->productLinksMock = $this->getMockBuilder(ProductLinks::class) ->disableOriginalConstructor() ->getMock(); @@ -196,14 +194,10 @@ class HelperTest extends \PHPUnit_Framework_TestCase */ public function testInitialize() { - $this->customOptionMock->expects($this->once()) - ->method('setProductSku'); - $this->customOptionMock->expects($this->once()) - ->method('setOptionId'); - $optionsData = [ - 'option1' => ['is_delete' => true, 'name' => 'name1', 'price' => 'price1'], - 'option2' => ['is_delete' => false, 'name' => 'name1', 'price' => 'price1'], + 'option1' => ['is_delete' => true, 'name' => 'name1', 'price' => 'price1', 'option_id' => ''], + 'option2' => ['is_delete' => false, 'name' => 'name1', 'price' => 'price1', 'option_id' => '13'], + 'option3' => ['is_delete' => false, 'name' => 'name1', 'price' => 'price1', 'option_id' => '14'] ]; $productData = [ 'stock_data' => ['stock_data'], @@ -277,14 +271,7 @@ class HelperTest extends \PHPUnit_Framework_TestCase ->method('getAttributes') ->willReturn($attributesArray); - $productData['category_ids'] = []; - $productData['website_ids'] = []; - unset($productData['options']); - - $this->productMock->expects($this->once()) - ->method('addData') - ->with($productData); - $this->productMock->expects($this->once()) + $this->productMock->expects($this->any()) ->method('getSku') ->willReturn('sku'); $this->productMock->expects($this->any()) @@ -293,13 +280,25 @@ class HelperTest extends \PHPUnit_Framework_TestCase $this->customOptionFactoryMock->expects($this->any()) ->method('create') - ->with(['data' => $optionsData['option2']]) - ->willReturn($this->customOptionMock); - $this->productMock->expects($this->once()) - ->method('setOptions') - ->with([$this->customOptionMock]); + ->willReturnMap([ + [ + ['data' => $optionsData['option2']], + (clone $this->customOptionMock)->setData($optionsData['option2']) + ], [ + ['data' => $optionsData['option3']], + (clone $this->customOptionMock)->setData($optionsData['option3']) + ] + ]); $this->assertEquals($this->productMock, $this->helper->initialize($this->productMock)); + + $productOptions = $this->productMock->getOptions(); + $this->assertTrue(2 == count($productOptions)); + list($option2, $option3) = $productOptions; + $this->assertTrue($option2->getOptionId() == $optionsData['option2']['option_id']); + $this->assertTrue('sku' == $option2->getData('product_sku')); + $this->assertTrue($option3->getOptionId() == $optionsData['option3']['option_id']); + $this->assertTrue('sku' == $option2->getData('product_sku')); } /** @@ -362,9 +361,9 @@ class HelperTest extends \PHPUnit_Framework_TestCase [ 'option_id' => '5', 'key1' => 'val1', - 'key2' => 'val2', + 'title' => 'val2', 'default_key1' => 'val3', - 'default_key2' => 'val4', + 'default_title' => 'val4', 'values' => [ [ 'option_type_id' => '2', @@ -379,7 +378,7 @@ class HelperTest extends \PHPUnit_Framework_TestCase [ 5 => [ 'key1' => '0', - 'key2' => '1', + 'title' => '1', 'values' => [2 => ['key1' => 1]] ] ], @@ -387,9 +386,10 @@ class HelperTest extends \PHPUnit_Framework_TestCase [ 'option_id' => '5', 'key1' => 'val1', - 'key2' => 'val4', + 'title' => 'val4', 'default_key1' => 'val3', - 'default_key2' => 'val4', + 'default_title' => 'val4', + 'is_delete_store_title' => 1, 'values' => [ [ 'option_type_id' => '2', @@ -413,8 +413,9 @@ class HelperTest extends \PHPUnit_Framework_TestCase [ 'option_type_id' => '2', 'key1' => 'val1', - 'key2' => 'val2', - 'default_key1' => 'val11' + 'title' => 'val2', + 'default_key1' => 'val11', + 'default_title' => 'val22' ] ] ] @@ -423,7 +424,7 @@ class HelperTest extends \PHPUnit_Framework_TestCase 7 => [ 'key1' => '1', 'key2' => '1', - 'values' => [2 => ['key1' => 1, 'key2' => 1]] + 'values' => [2 => ['key1' => 0, 'title' => 1]] ] ], [ @@ -435,9 +436,11 @@ class HelperTest extends \PHPUnit_Framework_TestCase 'values' => [ [ 'option_type_id' => '2', - 'key1' => 'val11', - 'key2' => 'val2', - 'default_key1' => 'val11' + 'key1' => 'val1', + 'title' => 'val22', + 'default_key1' => 'val11', + 'default_title' => 'val22', + 'is_delete_store_title' => 1 ] ] ] diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/FlatTableBuilderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/FlatTableBuilderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c57dd950dc3e01c038631b52d2eb76657f1c25b1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/FlatTableBuilderTest.php @@ -0,0 +1,204 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Model\Indexer\Product\Flat; + +use Magento\Catalog\Api\Data\ProductInterface; + +/** + * Class FlatTableBuilderTest + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class FlatTableBuilderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Catalog\Helper\Product\Flat\Indexer|\PHPUnit_Framework_MockObject_MockObject + */ + private $flatIndexerMock; + + /** + * @var \Magento\Framework\App\ResourceConnection|\PHPUnit_Framework_MockObject_MockObject + */ + private $resourceMock; + + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $scopeConfigMock; + + /** + * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $storeManagerMock; + + /** + * @var \Magento\Catalog\Model\Indexer\Product\Flat\TableDataInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $tableDataMock; + + /** + * @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $connectionMock; + + /** + * @var \Magento\Framework\EntityManager\MetadataPool|\PHPUnit_Framework_MockObject_MockObject + */ + private $metadataPoolMock; + + /** + * @var \Magento\Framework\EntityManager\EntityMetadataInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $metadataMock; + + /** + * @var \Magento\Catalog\Model\Indexer\Product\Flat\FlatTableBuilder + */ + private $flatTableBuilder; + + protected function setUp() + { + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->flatIndexerMock = $this->getMockBuilder(\Magento\Catalog\Helper\Product\Flat\Indexer::class) + ->disableOriginalConstructor() + ->getMock(); + $this->resourceMock = $this->getMockBuilder(\Magento\Framework\App\ResourceConnection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->scopeConfigMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->tableDataMock = $this->getMockBuilder( + \Magento\Catalog\Model\Indexer\Product\Flat\TableDataInterface::class + )->disableOriginalConstructor()->getMockForAbstractClass(); + $this->connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->metadataPoolMock = $this->getMockBuilder(\Magento\Framework\EntityManager\MetadataPool::class) + ->disableOriginalConstructor() + ->getMock(); + $this->metadataMock = $this->getMockBuilder( + \Magento\Framework\EntityManager\EntityMetadataInterface::class + )->disableOriginalConstructor()->getMockForAbstractClass(); + $this->metadataMock->expects($this->any())->method('getLinkField')->willReturn('entity_id'); + + $this->flatTableBuilder = $objectManagerHelper->getObject( + \Magento\Catalog\Model\Indexer\Product\Flat\FlatTableBuilder::class, + [ + 'productIndexerHelper' => $this->flatIndexerMock, + 'resource' => $this->resourceMock, + 'config' => $this->scopeConfigMock, + 'storeManager' => $this->storeManagerMock, + 'tableData' => $this->tableDataMock, + '_connection' => $this->connectionMock + ] + ); + $objectManagerHelper->setBackwardCompatibleProperty( + $this->flatTableBuilder, + 'metadataPool', + $this->metadataPoolMock + ); + } + + public function testBuild() + { + list($storeId, $changedIds, $valueFieldSuffix, $tableDropSuffix, $fillTmpTables) = [1, [], '', '', true]; + $tableName = 'catalog_product_entity'; + $attributeTable = 'catalog_product_entity_int'; + $temporaryTableName = 'catalog_product_entity_int_tmp_indexer'; + $temporaryValueTableName = 'catalog_product_entity_int_tmp_indexer'; + $linkField = 'entity_id'; + $statusId = 22; + $this->flatIndexerMock->expects($this->once())->method('getAttributes')->willReturn([]); + $this->flatIndexerMock->expects($this->exactly(3))->method('getFlatColumns') + ->willReturnOnConsecutiveCalls( + [], + [$linkField => []], + [$linkField => []] + ); + $this->flatIndexerMock->expects($this->once())->method('getFlatIndexes')->willReturn([]); + $statusAttributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute::class) + ->disableOriginalConstructor() + ->getMock(); + $this->flatIndexerMock->expects($this->once())->method('getTablesStructure') + ->willReturn( + [ + 'catalog_product_entity' => [ + $linkField => $statusAttributeMock + ], + 'catalog_product_entity_int' => [ + $linkField => $statusAttributeMock + ] + ] + ); + $this->flatIndexerMock->expects($this->atLeastOnce())->method('getTable') + ->withConsecutive( + [$tableName], + ['catalog_product_website'] + ) + ->willReturn( + $tableName, + 'catalog_product_website' + ); + $this->flatIndexerMock->expects($this->once())->method('getAttribute') + ->with('status') + ->willReturn($statusAttributeMock); + $backendMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend::class) + ->disableOriginalConstructor() + ->getMock(); + $backendMock->expects($this->atLeastOnce())->method('getTable')->willReturn($attributeTable); + $statusAttributeMock->expects($this->atLeastOnce())->method('getBackend')->willReturn( + $backendMock + ); + $statusAttributeMock->expects($this->atLeastOnce())->method('getId')->willReturn($statusId); + $tableMock = $this->getMockBuilder(\Magento\Framework\DB\Ddl\Table::class) + ->disableOriginalConstructor() + ->getMock(); + $this->connectionMock->expects($this->any())->method('newTable')->willReturn($tableMock); + $selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class) + ->disableOriginalConstructor() + ->getMock(); + $this->connectionMock->expects($this->atLeastOnce())->method('select')->willReturn($selectMock); + $selectMock->expects($this->once())->method('from')->with( + ['et' => 'catalog_product_entity_tmp_indexer'], + [$linkField, 'type_id', 'attribute_set_id'] + )->willReturnSelf(); + $selectMock->expects($this->atLeastOnce())->method('joinInner')->willReturnSelf(); + $selectMock->expects($this->exactly(3))->method('joinLeft') + ->withConsecutive( + [ + ['dstatus' => $attributeTable], + sprintf( + 'e.%s = dstatus.%s AND dstatus.store_id = %s AND dstatus.attribute_id = %s', + $linkField, + $linkField, + $storeId, + $statusId + ), + [] + ], + [ + $temporaryTableName, + "e.{$linkField} = ${temporaryTableName}.{$linkField}", + [$linkField] + ], + [ + $temporaryValueTableName, + "e.${linkField} = " . $temporaryValueTableName . ".${linkField}", + [$linkField] + ] + )->willReturnSelf(); + $this->metadataPoolMock->expects($this->atLeastOnce())->method('getMetadata')->with(ProductInterface::class) + ->willReturn($this->metadataMock); + $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->storeManagerMock->expects($this->once())->method('getStore')->with($storeId)->willReturn($storeMock); + $this->flatTableBuilder->build($storeId, $changedIds, $valueFieldSuffix, $tableDropSuffix, $fillTmpTables); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Table/BuilderTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Table/BuilderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0a80250fbdf03153b52b4e509b90bee9bf7dde9c --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Indexer/Product/Flat/Table/BuilderTest.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Model\Indexer\Product\Flat\Table; + +/** + * Class BuilderTest + */ +class BuilderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $connectionMock; + + public function testAddColumn() + { + $this->connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $table = $this->getMockBuilder(\Magento\Framework\DB\Ddl\Table::class) + ->disableOriginalConstructor() + ->getMock(); + $table->expects($this->once())->method('addColumn') + ->with('test', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER) + ->willReturnSelf(); + $tableName = 'test_table'; + $this->connectionMock->expects($this->once()) + ->method('newTable') + ->with($tableName) + ->willReturn($table); + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + /** + * @var $builder \Magento\Catalog\Model\Indexer\Product\Flat\Table\Builder + */ + $builder = $objectManagerHelper->getObject( + \Magento\Catalog\Model\Indexer\Product\Flat\Table\Builder::class, + [ + 'connection' => $this->connectionMock, + 'tableName' => $tableName + ] + ); + $this->assertEquals($builder, $builder->addColumn('test', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER)); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/RepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/RepositoryTest.php index c461e7a616d81c3e9a72a3031d979b9f44817de1..894596342722d392fd79b51a330341c0b14660f5 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/RepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/RepositoryTest.php @@ -8,6 +8,8 @@ namespace Magento\Catalog\Test\Unit\Model\Product\Option; use \Magento\Catalog\Model\Product\Option\Repository; +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use \Magento\Catalog\Model\ResourceModel\Product\Option\CollectionFactory; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -30,10 +32,15 @@ class RepositoryTest extends \PHPUnit_Framework_TestCase protected $optionResourceMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ProductCustomOptionInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $optionMock; + /** + * @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $optionCollectionFactory; + /** * @var \PHPUnit_Framework_MockObject_MockObject */ @@ -71,13 +78,10 @@ class RepositoryTest extends \PHPUnit_Framework_TestCase '', false ); - $optionCollectionFactory = $this->getMock( - \Magento\Catalog\Model\ResourceModel\Product\Option\CollectionFactory::class, - ['create'], - [], - '', - false - ); + $this->optionCollectionFactory = $this->getMockBuilder(CollectionFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); $metadataPool = $this->getMockBuilder(\Magento\Framework\EntityManager\MetadataPool::class) ->disableOriginalConstructor() ->getMock(); @@ -96,7 +100,7 @@ class RepositoryTest extends \PHPUnit_Framework_TestCase $this->optionRepository, [ 'optionFactory' => $optionFactory, - 'optionCollectionFactory' => $optionCollectionFactory, + 'collectionFactory' => $this->optionCollectionFactory, 'metadataPool' => $metadataPool ] ); @@ -240,4 +244,75 @@ class RepositoryTest extends \PHPUnit_Framework_TestCase } } } + + /** + * @expectedException \Magento\Framework\Exception\CouldNotSaveException + * @expectedExceptionMessage ProductSku should be specified + */ + public function testSaveCouldNotSaveException() + { + $this->optionMock->expects($this->once())->method('getProductSku')->willReturn(null); + $this->optionRepository->save($this->optionMock); + } + + /** + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + */ + public function testSaveNoSuchEntityException() + { + $productSku = 'simple_product'; + $optionId = 1; + $productOptionId = 2; + $this->optionMock->expects($this->once())->method('getProductSku')->willReturn($productSku); + $this->productRepositoryMock + ->expects($this->once()) + ->method('get') + ->with($productSku) + ->willReturn($this->productMock); + $productOption = clone $this->optionMock; + $this->optionMock->expects($this->any())->method('getOptionId')->willReturn($optionId); + $productOption->expects($this->any())->method('getOptionId')->willReturn($productOptionId); + $this->productMock->expects($this->once())->method('getOptions')->willReturn([$productOption]); + $this->optionRepository->save($this->optionMock); + } + + public function testSave() + { + $productSku = 'simple_product'; + $optionId = 1; + $originalValue1 = $this->getMockBuilder(\Magento\Catalog\Model\Product\Option::class) + ->disableOriginalConstructor() + ->getMock(); + $originalValue2 = clone $originalValue1; + $originalValue3 = clone $originalValue1; + + $originalValue1->expects($this->at(0))->method('getData')->with('option_type_id')->willReturn(10); + $originalValue1->expects($this->once())->method('setData')->with('is_delete', 1); + $originalValue2->expects($this->once())->method('getData')->with('option_type_id')->willReturn(4); + $originalValue3->expects($this->once())->method('getData')->with('option_type_id')->willReturn(5); + + $this->optionMock->expects($this->once())->method('getProductSku')->willReturn($productSku); + $this->productRepositoryMock + ->expects($this->once()) + ->method('get') + ->with($productSku) + ->willReturn($this->productMock); + $this->optionMock->expects($this->any())->method('getOptionId')->willReturn($optionId); + $this->productMock->expects($this->once())->method('getOptions')->willReturn([]); + $this->optionMock->expects($this->once())->method('getData')->with('values')->willReturn([ + ['option_type_id' => 4], + ['option_type_id' => 5] + ]); + $optionCollection = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Product\Option\Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $optionCollection->expects($this->once())->method('getProductOptions')->willReturn([$this->optionMock]); + $this->optionCollectionFactory->expects($this->once())->method('create')->willReturn($optionCollection); + $this->optionMock->expects($this->once())->method('getValues')->willReturn([ + $originalValue1, + $originalValue2, + $originalValue3 + ]); + $this->assertEquals($this->optionMock, $this->optionRepository->save($this->optionMock)); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/SaveHandlerTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/SaveHandlerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..222abadd9fe4b9ff2e6c82a9bd7efc24bf5166de --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/SaveHandlerTest.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Unit\Model\Product\Option; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Option; +use \Magento\Catalog\Model\Product\Option\Repository; +use \Magento\Catalog\Model\Product\Option\SaveHandler; + +class SaveHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var SaveHandler|\PHPUnit_Framework_MockObject_MockObject + */ + protected $model; + + /** + * @var Product|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entity; + + /** + * @var Option|\PHPUnit_Framework_MockObject_MockObject + */ + protected $optionMock; + + /** + * @var Repository|\PHPUnit_Framework_MockObject_MockObject + */ + protected $optionRepository; + + public function setUp() + { + $this->entity = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->getMock(); + $this->optionMock = $this->getMockBuilder(Option::class) + ->disableOriginalConstructor() + ->getMock(); + $this->optionRepository = $this->getMockBuilder(Repository::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->model = new SaveHandler($this->optionRepository); + } + + public function testExecute() + { + $this->optionMock->expects($this->any())->method('getOptionId')->willReturn(5); + $this->entity->expects($this->once())->method('getOptions')->willReturn([$this->optionMock]); + + $secondOptionMock = $this->getMockBuilder(Option::class) + ->disableOriginalConstructor() + ->getMock(); + $secondOptionMock->expects($this->once())->method('getOptionId')->willReturn(6); + + $this->optionRepository + ->expects($this->once()) + ->method('getProductOptions') + ->with($this->entity) + ->willReturn([$this->optionMock, $secondOptionMock]); + + $this->optionRepository->expects($this->once())->method('delete'); + $this->optionRepository->expects($this->once())->method('save')->with($this->optionMock); + + $this->assertEquals($this->entity, $this->model->execute($this->entity)); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CustomOptionsTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CustomOptionsTest.php index c2b3de0c24cb0736b778efb75d9e722d0fcb47b8..75a2b7c27b4d263d6603438ae9d7c8ab13578e91 100644 --- a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CustomOptionsTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/CustomOptionsTest.php @@ -87,35 +87,47 @@ class CustomOptionsTest extends AbstractModifierTest $originalData = [ $productId => [ - \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions::DATA_SOURCE_DEFAULT => [ + CustomOptions::DATA_SOURCE_DEFAULT => [ 'title' => 'original' ] ] ]; $options = [ - $this->getProductOptionMock(['title' => 'option1']), + $this->getProductOptionMock(['title' => 'option1', 'store_title' => 'Option Store Title']), $this->getProductOptionMock( - ['title' => 'option2'], + ['title' => 'option2', 'store_title' => null], [ - $this->getProductOptionMock(['title' => 'value1']), - $this->getProductOptionMock(['title' => 'value2']) + $this->getProductOptionMock(['title' => 'value1', 'store_title' => 'Option Value Store Title']), + $this->getProductOptionMock(['title' => 'value2', 'store_title' => null]) ] ) ]; $resultData = [ $productId => [ - \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions::DATA_SOURCE_DEFAULT => [ - 'title' => 'original', - \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions::FIELD_ENABLE => 1, - \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions::GRID_OPTIONS_NAME => [ - ['title' => 'option1'], + CustomOptions::DATA_SOURCE_DEFAULT => [ + CustomOptions::FIELD_TITLE_NAME => 'original', + CustomOptions::FIELD_ENABLE => 1, + CustomOptions::GRID_OPTIONS_NAME => [ [ - 'title' => 'option2', + CustomOptions::FIELD_TITLE_NAME => 'option1', + CustomOptions::FIELD_STORE_TITLE_NAME => 'Option Store Title', + CustomOptions::FIELD_IS_USE_DEFAULT => false + ], [ + CustomOptions::FIELD_TITLE_NAME => 'option2', + CustomOptions::FIELD_STORE_TITLE_NAME => null, + CustomOptions::FIELD_IS_USE_DEFAULT => true, CustomOptions::GRID_TYPE_SELECT_NAME => [ - ['title' => 'value1'], - ['title' => 'value2'] + [ + CustomOptions::FIELD_TITLE_NAME => 'value1', + CustomOptions::FIELD_STORE_TITLE_NAME => 'Option Value Store Title', + CustomOptions::FIELD_IS_USE_DEFAULT => false + ], [ + CustomOptions::FIELD_TITLE_NAME => 'value2', + CustomOptions::FIELD_STORE_TITLE_NAME => null, + CustomOptions::FIELD_IS_USE_DEFAULT => true + ] ] ] ] @@ -154,13 +166,13 @@ class CustomOptionsTest extends AbstractModifierTest */ protected function getProductOptionMock(array $data, array $values = []) { + /** @var ProductOption|\PHPUnit_Framework_MockObject_MockObject $productOptionMock */ $productOptionMock = $this->getMockBuilder(ProductOption::class) ->disableOriginalConstructor() + ->setMethods(['getValues']) ->getMock(); - $productOptionMock->expects($this->any()) - ->method('getData') - ->willReturn($data); + $productOptionMock->setData($data); $productOptionMock->expects($this->any()) ->method('getValues') ->willReturn($values); 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 5e6c88a1ba93d43f3ab05236769761aa17fcfb12..780cdbbb00e0a3ac06208e6c70a01708376f071d 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 @@ -68,6 +68,7 @@ class CustomOptions extends AbstractModifier const FIELD_ENABLE = 'affect_product_custom_options'; const FIELD_OPTION_ID = 'option_id'; const FIELD_TITLE_NAME = 'title'; + const FIELD_STORE_TITLE_NAME = 'store_title'; const FIELD_TYPE_NAME = 'type'; const FIELD_IS_REQUIRE_NAME = 'is_require'; const FIELD_SORT_ORDER_NAME = 'sort_order'; @@ -79,6 +80,7 @@ class CustomOptions extends AbstractModifier const FIELD_IMAGE_SIZE_X_NAME = 'image_size_x'; const FIELD_IMAGE_SIZE_Y_NAME = 'image_size_y'; const FIELD_IS_DELETE = 'is_delete'; + const FIELD_IS_USE_DEFAULT = 'is_use_default'; /**#@-*/ /**#@+ @@ -112,7 +114,7 @@ class CustomOptions extends AbstractModifier * @var UrlInterface */ protected $urlBuilder; - + /** * @var ArrayManager */ @@ -162,9 +164,14 @@ class CustomOptions extends AbstractModifier /** @var \Magento\Catalog\Model\Product\Option $option */ foreach ($productOptions as $index => $option) { - $options[$index] = $this->formatPriceByPath(static::FIELD_PRICE_NAME, $option->getData()); + $optionData = $option->getData(); + $optionData[static::FIELD_IS_USE_DEFAULT] = !$option->getData(static::FIELD_STORE_TITLE_NAME); + $options[$index] = $this->formatPriceByPath(static::FIELD_PRICE_NAME, $optionData); $values = $option->getValues() ?: []; + foreach ($values as $value) { + $value->setData(static::FIELD_IS_USE_DEFAULT, !$value->getData(static::FIELD_STORE_TITLE_NAME)); + } /** @var \Magento\Catalog\Model\Product\Option $value */ foreach ($values as $value) { $options[$index][static::GRID_TYPE_SELECT_NAME][] = $this->formatPriceByPath( @@ -529,7 +536,8 @@ class CustomOptions extends AbstractModifier 'component' => 'Magento_Catalog/component/static-type-input', 'valueUpdate' => 'input', 'imports' => [ - 'optionId' => '${ $.provider }:${ $.parentScope }.option_id' + 'optionId' => '${ $.provider }:${ $.parentScope }.option_id', + 'isUseDefault' => '${ $.provider }:${ $.parentScope }.is_use_default' ] ], ], @@ -604,6 +612,7 @@ class CustomOptions extends AbstractModifier 'imports' => [ 'optionId' => '${ $.provider }:${ $.parentScope }.option_id', 'optionTypeId' => '${ $.provider }:${ $.parentScope }.option_type_id', + 'isUseDefault' => '${ $.provider }:${ $.parentScope }.is_use_default' ], 'service' => [ 'template' => 'Magento_Catalog/form/element/helper/custom-option-type-service', @@ -1109,7 +1118,7 @@ class CustomOptions extends AbstractModifier } return $this->localeCurrency; } - + /** * Format price according to the locale of the currency * diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 49ad7d67d705899077d4beb13f4a26383982dcf5..b355dd2601c3da3a62ad21c0ccf0267e0c2ef809 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -47,6 +47,7 @@ <preference for="Magento\Catalog\Api\CategoryListInterface" type="Magento\Catalog\Model\CategoryList" /> <preference for="Magento\Catalog\Api\Data\CategorySearchResultsInterface" type="Magento\Framework\Api\SearchResults" /> <preference for="Magento\Catalog\Model\Config\Source\ProductPriceOptionsInterface" type="Magento\Catalog\Model\Config\Source\Product\Options\Price"/> + <preference for="Magento\Catalog\Model\Indexer\Product\Flat\Table\BuilderInterface" type="Magento\Catalog\Model\Indexer\Product\Flat\Table\Builder"/> <type name="Magento\Customer\Model\ResourceModel\Visitor"> <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" /> </type> diff --git a/app/code/Magento/Catalog/view/adminhtml/web/template/form/element/helper/custom-option-type-service.html b/app/code/Magento/Catalog/view/adminhtml/web/template/form/element/helper/custom-option-type-service.html index a3f7970e7420ad52ceb96ed23943f8166d1e2c4a..442d96b5ae4bac2c2954f377bba818448d1284b9 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/template/form/element/helper/custom-option-type-service.html +++ b/app/code/Magento/Catalog/view/adminhtml/web/template/form/element/helper/custom-option-type-service.html @@ -12,6 +12,6 @@ name: 'options_use_default[' + $data.optionId + '][values][' + $data.optionTypeId + '][' + $data.index + ']', 'data-form-part': $data.ns " - ko-checked="isUseDefault"> + ko-checked="$data.isUseDefault"> <label translate="'Use Default Value'" attr="for: $data.uid + '_default'" class="admin__field-label"></label> </div> diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index b37ba530845e579cd5b91970e4255eee1745696d..a0feddec6529441ee5f3530bde7db9636fc88895 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -273,7 +273,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity ValidatorInterface::ERROR_MEDIA_PATH_NOT_ACCESSIBLE => 'Imported resource (image) does not exist in the local media storage', ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE => 'Imported resource (image) could not be downloaded from external resource due to timeout or access permissions', ValidatorInterface::ERROR_INVALID_WEIGHT => 'Product weight is invalid', - ValidatorInterface::ERROR_DUPLICATE_URL_KEY => 'Specified URL key already exists', + ValidatorInterface::ERROR_DUPLICATE_URL_KEY => 'Url key: \'%s\' was already generated for an item with the SKU: \'%s\'. You need to specify the unique URL key manually' ]; /** @@ -2367,7 +2367,17 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $this->urlKeys[$storeId][$urlPath] = $rowData[self::COL_SKU]; $this->rowNumbers[$storeId][$urlPath] = $rowNum; } else { - $this->addRowError(ValidatorInterface::ERROR_DUPLICATE_URL_KEY, $rowNum); + $message = sprintf( + $this->retrieveMessageTemplate(ValidatorInterface::ERROR_DUPLICATE_URL_KEY), + $urlKey, + $this->urlKeys[$storeId][$urlPath] + ); + $this->addRowError( + ValidatorInterface::ERROR_DUPLICATE_URL_KEY, + $rowNum, + $rowData[self::COL_NAME], + $message + ); } } } diff --git a/app/code/Magento/Eav/etc/di.xml b/app/code/Magento/Eav/etc/di.xml index e273eab770c170c5ab6ff6b27da18ecdf41fc7c6..0fcba9907cc4d46b76cf0b7e86c2e0e1bf61da5a 100644 --- a/app/code/Magento/Eav/etc/di.xml +++ b/app/code/Magento/Eav/etc/di.xml @@ -152,9 +152,12 @@ <argument name="collectionProcessor" xsi:type="object">Magento\Eav\Model\Api\SearchCriteria\AttributeGroupCollectionProcessor</argument> </arguments> </type> - <type name="Magento\Framework\EntityManager\CustomAttributesMapper"> + <type name="Magento\Framework\EntityManager\CompositeMapper"> <arguments> - <argument name="mapper" xsi:type="object">Magento\Eav\Model\CustomAttributesMapper</argument> + <argument name="mappers" xsi:type="array"> + <item name="mapper" xsi:type="object">Magento\Framework\EntityManager\Mapper</item> + <item name="customAttributesMapper" xsi:type="object">Magento\Eav\Model\CustomAttributesMapper</item> + </argument> </arguments> </type> </config> diff --git a/app/code/Magento/Sales/Model/InvoiceOrder.php b/app/code/Magento/Sales/Model/InvoiceOrder.php index e51b46082d943d8ff609bacb80fae19c861afba4..c503b01a5ab21014683a7eaa5981f65fa16b558f 100644 --- a/app/code/Magento/Sales/Model/InvoiceOrder.php +++ b/app/code/Magento/Sales/Model/InvoiceOrder.php @@ -12,15 +12,12 @@ use Magento\Sales\Api\Data\InvoiceCreationArgumentsInterface; use Magento\Sales\Api\InvoiceOrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\Order\Config as OrderConfig; -use Magento\Sales\Model\Order\Invoice\InvoiceValidatorInterface; use Magento\Sales\Model\Order\Invoice\NotifierInterface; use Magento\Sales\Model\Order\InvoiceDocumentFactory; -use Magento\Sales\Model\Order\InvoiceQuantityValidator; use Magento\Sales\Model\Order\InvoiceRepository; use Magento\Sales\Model\Order\OrderStateResolverInterface; -use Magento\Sales\Model\Order\OrderValidatorInterface; use Magento\Sales\Model\Order\PaymentAdapterInterface; -use Magento\Sales\Model\Order\Validation\CanInvoice; +use Magento\Sales\Model\Order\Validation\InvoiceOrderInterface as InvoiceOrderValidator; use Psr\Log\LoggerInterface; /** @@ -44,11 +41,6 @@ class InvoiceOrder implements InvoiceOrderInterface */ private $invoiceDocumentFactory; - /** - * @var InvoiceValidatorInterface - */ - private $invoiceValidator; - /** * @var PaymentAdapterInterface */ @@ -69,6 +61,11 @@ class InvoiceOrder implements InvoiceOrderInterface */ private $invoiceRepository; + /** + * @var InvoiceOrderValidator + */ + private $invoiceOrderValidator; + /** * @var NotifierInterface */ @@ -80,21 +77,15 @@ class InvoiceOrder implements InvoiceOrderInterface private $logger; /** - * @var OrderValidatorInterface - */ - private $orderValidator; - - /** - * OrderInvoice constructor. + * InvoiceOrder constructor. * @param ResourceConnection $resourceConnection * @param OrderRepositoryInterface $orderRepository * @param InvoiceDocumentFactory $invoiceDocumentFactory - * @param InvoiceValidatorInterface $invoiceValidator - * @param OrderValidatorInterface $orderValidator * @param PaymentAdapterInterface $paymentAdapter * @param OrderStateResolverInterface $orderStateResolver * @param OrderConfig $config * @param InvoiceRepository $invoiceRepository + * @param InvoiceOrderValidator $invoiceOrderValidator * @param NotifierInterface $notifierInterface * @param LoggerInterface $logger * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -103,24 +94,22 @@ class InvoiceOrder implements InvoiceOrderInterface ResourceConnection $resourceConnection, OrderRepositoryInterface $orderRepository, InvoiceDocumentFactory $invoiceDocumentFactory, - InvoiceValidatorInterface $invoiceValidator, - OrderValidatorInterface $orderValidator, PaymentAdapterInterface $paymentAdapter, OrderStateResolverInterface $orderStateResolver, OrderConfig $config, InvoiceRepository $invoiceRepository, + InvoiceOrderValidator $invoiceOrderValidator, NotifierInterface $notifierInterface, LoggerInterface $logger ) { $this->resourceConnection = $resourceConnection; $this->orderRepository = $orderRepository; $this->invoiceDocumentFactory = $invoiceDocumentFactory; - $this->invoiceValidator = $invoiceValidator; - $this->orderValidator = $orderValidator; $this->paymentAdapter = $paymentAdapter; $this->orderStateResolver = $orderStateResolver; $this->config = $config; $this->invoiceRepository = $invoiceRepository; + $this->invoiceOrderValidator = $invoiceOrderValidator; $this->notifierInterface = $notifierInterface; $this->logger = $logger; } @@ -158,19 +147,19 @@ class InvoiceOrder implements InvoiceOrderInterface ($appendComment && $notify), $arguments ); - $errorMessages = array_merge( - $this->invoiceValidator->validate( - $invoice, - [InvoiceQuantityValidator::class] - ), - $this->orderValidator->validate( - $order, - [CanInvoice::class] - ) + $errorMessages = $this->invoiceOrderValidator->validate( + $order, + $invoice, + $capture, + $items, + $notify, + $appendComment, + $comment, + $arguments ); - if (!empty($errorMessages)) { + if ($errorMessages->hasMessages()) { throw new \Magento\Sales\Exception\DocumentValidationException( - __("Invoice Document Validation Error(s):\n" . implode("\n", $errorMessages)) + __("Invoice Document Validation Error(s):\n" . implode("\n", $errorMessages->getMessages())) ); } $connection->beginTransaction(); diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/CreditmemoValidatorInterface.php b/app/code/Magento/Sales/Model/Order/Creditmemo/CreditmemoValidatorInterface.php index 3889f3b985ff02603009084fa956fab65e505556..030a9a7d128de7d49d989c0e1af61a323786f5a5 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/CreditmemoValidatorInterface.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/CreditmemoValidatorInterface.php @@ -8,6 +8,7 @@ namespace Magento\Sales\Model\Order\Creditmemo; use Magento\Sales\Api\Data\CreditmemoInterface; use Magento\Sales\Exception\DocumentValidationException; use Magento\Sales\Model\ValidatorInterface; +use Magento\Sales\Model\ValidatorResultInterface; /** * Interface CreditmemoValidatorInterface @@ -17,7 +18,7 @@ interface CreditmemoValidatorInterface /** * @param CreditmemoInterface $entity * @param ValidatorInterface[] $validators - * @return string[] + * @return ValidatorResultInterface * @throws DocumentValidationException */ public function validate(CreditmemoInterface $entity, array $validators); diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/ItemCreationValidatorInterface.php b/app/code/Magento/Sales/Model/Order/Creditmemo/ItemCreationValidatorInterface.php index 9f8bb84ccd16a175ee23de755169281026dc053b..7a758122b8aac13d75dcf5784ef85e71d2298b2d 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/ItemCreationValidatorInterface.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/ItemCreationValidatorInterface.php @@ -7,6 +7,7 @@ namespace Magento\Sales\Model\Order\Creditmemo; use Magento\Sales\Api\Data\CreditmemoItemCreationInterface; use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\ValidatorResultInterface; /** * Interface ItemCreationValidatorInterface @@ -17,7 +18,7 @@ interface ItemCreationValidatorInterface * @param CreditmemoItemCreationInterface $item * @param array $validators * @param OrderInterface|null $context - * @return mixed + * @return ValidatorResultInterface */ public function validate(CreditmemoItemCreationInterface $item, array $validators, OrderInterface $context = null); } diff --git a/app/code/Magento/Sales/Model/Order/CreditmemoDocumentFactory.php b/app/code/Magento/Sales/Model/Order/CreditmemoDocumentFactory.php index 469b226053cdd4e2b0658dcd596d7d740572f0c7..816c3df3fc1f0c6b045f26f2ebb79e769c1be031 100644 --- a/app/code/Magento/Sales/Model/Order/CreditmemoDocumentFactory.php +++ b/app/code/Magento/Sales/Model/Order/CreditmemoDocumentFactory.php @@ -7,6 +7,8 @@ namespace Magento\Sales\Model\Order; /** * Class CreditmemoDocumentFactory + * + * @api */ class CreditmemoDocumentFactory { diff --git a/app/code/Magento/Sales/Model/Order/Invoice/InvoiceValidatorInterface.php b/app/code/Magento/Sales/Model/Order/Invoice/InvoiceValidatorInterface.php index 568019a40fce5bc69ccab49ca8b3f4fee048505e..44d701b1426e79d3df390b46b7e76ee6743b691c 100644 --- a/app/code/Magento/Sales/Model/Order/Invoice/InvoiceValidatorInterface.php +++ b/app/code/Magento/Sales/Model/Order/Invoice/InvoiceValidatorInterface.php @@ -8,6 +8,7 @@ namespace Magento\Sales\Model\Order\Invoice; use Magento\Sales\Api\Data\InvoiceInterface; use Magento\Sales\Exception\DocumentValidationException; use Magento\Sales\Model\ValidatorInterface; +use Magento\Sales\Model\ValidatorResultInterface; /** * Interface InvoiceValidatorInterface @@ -17,7 +18,7 @@ interface InvoiceValidatorInterface /** * @param InvoiceInterface $entity * @param ValidatorInterface[] $validators - * @return string[] + * @return ValidatorResultInterface * @throws DocumentValidationException */ public function validate(InvoiceInterface $entity, array $validators); diff --git a/app/code/Magento/Sales/Model/Order/OrderValidatorInterface.php b/app/code/Magento/Sales/Model/Order/OrderValidatorInterface.php index c5a9a6c1d32963fcc223099efe1fcb81bb530464..dfc95043cbd33b214b46f26e7fcdc2a039e9272e 100644 --- a/app/code/Magento/Sales/Model/Order/OrderValidatorInterface.php +++ b/app/code/Magento/Sales/Model/Order/OrderValidatorInterface.php @@ -8,6 +8,7 @@ namespace Magento\Sales\Model\Order; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Exception\DocumentValidationException; use Magento\Sales\Model\ValidatorInterface; +use Magento\Sales\Model\ValidatorResultInterface; /** * Interface OrderValidatorInterface @@ -17,7 +18,7 @@ interface OrderValidatorInterface /** * @param OrderInterface $entity * @param ValidatorInterface[] $validators - * @return string[] + * @return ValidatorResultInterface * @throws DocumentValidationException */ public function validate(OrderInterface $entity, array $validators); diff --git a/app/code/Magento/Sales/Model/Order/Shipment/ShipmentValidatorInterface.php b/app/code/Magento/Sales/Model/Order/Shipment/ShipmentValidatorInterface.php index 198a4019bf6b898e0c85fd2e2ff7c7ff77c2a9f6..43501a5b133140390e539a0d81985a4a5437598b 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment/ShipmentValidatorInterface.php +++ b/app/code/Magento/Sales/Model/Order/Shipment/ShipmentValidatorInterface.php @@ -8,6 +8,7 @@ namespace Magento\Sales\Model\Order\Shipment; use Magento\Sales\Api\Data\ShipmentInterface; use Magento\Sales\Exception\DocumentValidationException; use Magento\Sales\Model\ValidatorInterface; +use Magento\Sales\Model\ValidatorResultInterface; /** * Interface ShipmentValidatorInterface @@ -17,7 +18,7 @@ interface ShipmentValidatorInterface /** * @param ShipmentInterface $shipment * @param ValidatorInterface[] $validators - * @return string[] + * @return ValidatorResultInterface * @throws DocumentValidationException */ public function validate(ShipmentInterface $shipment, array $validators); diff --git a/app/code/Magento/Sales/Model/Order/Validation/InvoiceOrder.php b/app/code/Magento/Sales/Model/Order/Validation/InvoiceOrder.php new file mode 100644 index 0000000000000000000000000000000000000000..d912793afa157fbbd997e89ce46c8b84aa5d6f75 --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/Validation/InvoiceOrder.php @@ -0,0 +1,79 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Model\Order\Validation; + +use Magento\Sales\Api\Data\InvoiceCommentCreationInterface; +use Magento\Sales\Api\Data\InvoiceCreationArgumentsInterface; +use Magento\Sales\Api\Data\InvoiceInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order\Invoice\InvoiceValidatorInterface; +use Magento\Sales\Model\Order\InvoiceQuantityValidator; +use Magento\Sales\Model\Order\OrderValidatorInterface; +use Magento\Sales\Model\ValidatorResultInterface; +use Magento\Sales\Model\ValidatorResultMerger; + +/** + * Class InvoiceOrder + * Validation for invoice order operation + */ +class InvoiceOrder implements InvoiceOrderInterface +{ + /** + * @var InvoiceValidatorInterface + */ + private $invoiceValidator; + + /** + * @var OrderValidatorInterface + */ + private $orderValidator; + + /** + * @var ValidatorResultMerger + */ + private $validatorResultMerger; + + /** + * InvoiceOrder constructor. + * @param InvoiceValidatorInterface $invoiceValidator + * @param OrderValidatorInterface $orderValidator + * @param ValidatorResultMerger $validatorResultMerger + */ + public function __construct( + InvoiceValidatorInterface $invoiceValidator, + OrderValidatorInterface $orderValidator, + ValidatorResultMerger $validatorResultMerger + ) { + $this->invoiceValidator = $invoiceValidator; + $this->orderValidator = $orderValidator; + $this->validatorResultMerger = $validatorResultMerger; + } + + /** + * @inheritdoc + */ + public function validate( + OrderInterface $order, + InvoiceInterface $invoice, + $capture = false, + array $items = [], + $notify = false, + $appendComment = false, + InvoiceCommentCreationInterface $comment = null, + InvoiceCreationArgumentsInterface $arguments = null + ) { + return $this->validatorResultMerger->merge( + $this->invoiceValidator->validate( + $invoice, + [InvoiceQuantityValidator::class] + ), + $this->orderValidator->validate( + $order, + [CanInvoice::class] + ) + ); + } +} diff --git a/app/code/Magento/Sales/Model/Order/Validation/InvoiceOrderInterface.php b/app/code/Magento/Sales/Model/Order/Validation/InvoiceOrderInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..5c27741a1985582ee547a21faf9747d68d2c35c4 --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/Validation/InvoiceOrderInterface.php @@ -0,0 +1,42 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Model\Order\Validation; + +use Magento\Sales\Api\Data\InvoiceCommentCreationInterface; +use Magento\Sales\Api\Data\InvoiceCreationArgumentsInterface; +use Magento\Sales\Api\Data\InvoiceInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\ValidatorResultInterface; + +/** + * Interface InvoiceOrderInterface + * + * @api + */ +interface InvoiceOrderInterface +{ + /** + * @param OrderInterface $order + * @param InvoiceInterface $invoice + * @param bool $capture + * @param array $items + * @param bool $notify + * @param bool $appendComment + * @param InvoiceCommentCreationInterface|null $comment + * @param InvoiceCreationArgumentsInterface|null $arguments + * @return ValidatorResultInterface + */ + public function validate( + OrderInterface $order, + InvoiceInterface $invoice, + $capture = false, + array $items = [], + $notify = false, + $appendComment = false, + InvoiceCommentCreationInterface $comment = null, + InvoiceCreationArgumentsInterface $arguments = null + ); +} diff --git a/app/code/Magento/Sales/Model/Order/Validation/RefundInvoice.php b/app/code/Magento/Sales/Model/Order/Validation/RefundInvoice.php new file mode 100644 index 0000000000000000000000000000000000000000..d6bc86005cf2c906154ca107cc97c4fac4242fd4 --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/Validation/RefundInvoice.php @@ -0,0 +1,123 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Model\Order\Validation; + +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\Data\InvoiceInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order\Invoice\InvoiceValidatorInterface; +use Magento\Sales\Model\Order\Creditmemo\CreditmemoValidatorInterface; +use Magento\Sales\Model\Order\Creditmemo\Item\Validation\CreationQuantityValidator; +use Magento\Sales\Model\Order\Creditmemo\ItemCreationValidatorInterface; +use Magento\Sales\Model\Order\Creditmemo\Validation\QuantityValidator; +use Magento\Sales\Model\Order\Creditmemo\Validation\TotalsValidator; +use Magento\Sales\Model\Order\OrderValidatorInterface; +use Magento\Sales\Model\ValidatorResultMerger; + +/** + * Class RefundInvoice + */ +class RefundInvoice implements RefundInvoiceInterface +{ + /** + * @var OrderValidatorInterface + */ + private $orderValidator; + + /** + * @var CreditmemoValidatorInterface + */ + private $creditmemoValidator; + + /** + * @var ItemCreationValidatorInterface + */ + private $itemCreationValidator; + + /** + * @var InvoiceValidatorInterface + */ + private $invoiceValidator; + + /** + * @var ValidatorResultMerger + */ + private $validatorResultMerger; + + /** + * RefundArguments constructor. + * @param OrderValidatorInterface $orderValidator + * @param CreditmemoValidatorInterface $creditmemoValidator + * @param ItemCreationValidatorInterface $itemCreationValidator + * @param InvoiceValidatorInterface $invoiceValidator + * @param ValidatorResultMerger $validatorResultMerger + */ + public function __construct( + OrderValidatorInterface $orderValidator, + CreditmemoValidatorInterface $creditmemoValidator, + ItemCreationValidatorInterface $itemCreationValidator, + InvoiceValidatorInterface $invoiceValidator, + ValidatorResultMerger $validatorResultMerger + ) { + $this->orderValidator = $orderValidator; + $this->creditmemoValidator = $creditmemoValidator; + $this->itemCreationValidator = $itemCreationValidator; + $this->invoiceValidator = $invoiceValidator; + $this->validatorResultMerger = $validatorResultMerger; + } + + /** + * @inheritdoc + */ + public function validate( + InvoiceInterface $invoice, + OrderInterface $order, + CreditmemoInterface $creditmemo, + array $items = [], + $isOnline = false, + $notify = false, + $appendComment = false, + \Magento\Sales\Api\Data\CreditmemoCommentCreationInterface $comment = null, + \Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface $arguments = null + ) { + $orderValidationResult = $this->orderValidator->validate( + $order, + [ + CanRefund::class + ] + ); + $creditmemoValidationResult = $this->creditmemoValidator->validate( + $creditmemo, + [ + QuantityValidator::class, + TotalsValidator::class + ] + ); + + $itemsValidation = []; + foreach ($items as $item) { + $itemsValidation[] = $this->itemCreationValidator->validate( + $item, + [CreationQuantityValidator::class], + $order + )->getMessages(); + } + + $invoiceValidationResult = $this->invoiceValidator->validate( + $invoice, + [ + \Magento\Sales\Model\Order\Invoice\Validation\CanRefund::class + ] + ); + + return $this->validatorResultMerger->merge( + $orderValidationResult, + $creditmemoValidationResult, + $invoiceValidationResult->getMessages(), + ...$itemsValidation + ); + } +} diff --git a/app/code/Magento/Sales/Model/Order/Validation/RefundInvoiceInterface.php b/app/code/Magento/Sales/Model/Order/Validation/RefundInvoiceInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..83acc9811bb89f5ed8cff566274eb54a39668ad9 --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/Validation/RefundInvoiceInterface.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Model\Order\Validation; + +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\Data\InvoiceInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\ValidatorResultInterface; + +/** + * Interface RefundInvoiceInterface + * + * @api + */ +interface RefundInvoiceInterface +{ + /** + * @param InvoiceInterface $invoice + * @param OrderInterface $order + * @param CreditmemoInterface $creditmemo + * @param array $items + * @param bool $isOnline + * @param bool $notify + * @param bool $appendComment + * @param \Magento\Sales\Api\Data\CreditmemoCommentCreationInterface|null $comment + * @param \Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface|null $arguments + * @return ValidatorResultInterface + */ + public function validate( + InvoiceInterface $invoice, + OrderInterface $order, + CreditmemoInterface $creditmemo, + array $items = [], + $isOnline = false, + $notify = false, + $appendComment = false, + \Magento\Sales\Api\Data\CreditmemoCommentCreationInterface $comment = null, + \Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface $arguments = null + ); +} diff --git a/app/code/Magento/Sales/Model/Order/Validation/RefundOrder.php b/app/code/Magento/Sales/Model/Order/Validation/RefundOrder.php new file mode 100644 index 0000000000000000000000000000000000000000..c2664fda6b9836df6c91a35aabd6c84ed54e5827 --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/Validation/RefundOrder.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Model\Order\Validation; + +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order\Creditmemo\CreditmemoValidatorInterface; +use Magento\Sales\Model\Order\Creditmemo\Item\Validation\CreationQuantityValidator; +use Magento\Sales\Model\Order\Creditmemo\ItemCreationValidatorInterface; +use Magento\Sales\Model\Order\Creditmemo\Validation\QuantityValidator; +use Magento\Sales\Model\Order\Creditmemo\Validation\TotalsValidator; +use Magento\Sales\Model\Order\OrderValidatorInterface; +use Magento\Sales\Model\ValidatorResultMerger; + +/** + * Class RefundOrder + */ +class RefundOrder implements RefundOrderInterface +{ + /** + * @var OrderValidatorInterface + */ + private $orderValidator; + + /** + * @var CreditmemoValidatorInterface + */ + private $creditmemoValidator; + + /** + * @var ItemCreationValidatorInterface + */ + private $itemCreationValidator; + + /** + * @var ValidatorResultMerger + */ + private $validatorResultMerger; + + /** + * RefundArguments constructor. + * + * @param OrderValidatorInterface $orderValidator + * @param CreditmemoValidatorInterface $creditmemoValidator + * @param ItemCreationValidatorInterface $itemCreationValidator + * @param ValidatorResultMerger $validatorResultMerger + */ + public function __construct( + OrderValidatorInterface $orderValidator, + CreditmemoValidatorInterface $creditmemoValidator, + ItemCreationValidatorInterface $itemCreationValidator, + ValidatorResultMerger $validatorResultMerger + ) { + $this->orderValidator = $orderValidator; + $this->creditmemoValidator = $creditmemoValidator; + $this->itemCreationValidator = $itemCreationValidator; + $this->validatorResultMerger = $validatorResultMerger; + } + + /** + * @inheritdoc + */ + public function validate( + OrderInterface $order, + CreditmemoInterface $creditmemo, + array $items = [], + $notify = false, + $appendComment = false, + \Magento\Sales\Api\Data\CreditmemoCommentCreationInterface $comment = null, + \Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface $arguments = null + ) { + $orderValidationResult = $this->orderValidator->validate( + $order, + [ + CanRefund::class + ] + ); + $creditmemoValidationResult = $this->creditmemoValidator->validate( + $creditmemo, + [ + QuantityValidator::class, + TotalsValidator::class + ] + ); + + $itemsValidation = []; + foreach ($items as $item) { + $itemsValidation[] = $this->itemCreationValidator->validate( + $item, + [CreationQuantityValidator::class], + $order + )->getMessages(); + } + + return $this->validatorResultMerger->merge( + $orderValidationResult, + $creditmemoValidationResult, + ...$itemsValidation + ); + } +} diff --git a/app/code/Magento/Sales/Model/Order/Validation/RefundOrderInterface.php b/app/code/Magento/Sales/Model/Order/Validation/RefundOrderInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..2f770d20b513425dd67303490d67bb3d05fbc515 --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/Validation/RefundOrderInterface.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Model\Order\Validation; + +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\ValidatorResultInterface; + +/** + * Interface RefundOrderInterface + * + * @api + */ +interface RefundOrderInterface +{ + /** + * @param OrderInterface $order + * @param CreditmemoInterface $creditmemo + * @param array $items + * @param bool $notify + * @param bool $appendComment + * @param \Magento\Sales\Api\Data\CreditmemoCommentCreationInterface|null $comment + * @param \Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface|null $arguments + * @return ValidatorResultInterface + */ + public function validate( + OrderInterface $order, + CreditmemoInterface $creditmemo, + array $items = [], + $notify = false, + $appendComment = false, + \Magento\Sales\Api\Data\CreditmemoCommentCreationInterface $comment = null, + \Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface $arguments = null + ); +} diff --git a/app/code/Magento/Sales/Model/Order/Validation/ShipOrder.php b/app/code/Magento/Sales/Model/Order/Validation/ShipOrder.php new file mode 100644 index 0000000000000000000000000000000000000000..984c5f26fa7dbe50234b6f2b075397fb40f62ca1 --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/Validation/ShipOrder.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Model\Order\Validation; + +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\Data\ShipmentInterface; +use Magento\Sales\Model\Order\Shipment\Validation\QuantityValidator; +use Magento\Sales\Model\Order\OrderValidatorInterface; +use Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface; +use Magento\Sales\Model\Order\Shipment\Validation\TrackValidator; +use Magento\Sales\Model\ValidatorResultMerger; + +/** + * Class ShipOrder + */ +class ShipOrder implements ShipOrderInterface +{ + /** + * @var OrderValidatorInterface + */ + private $orderValidator; + + /** + * @var ShipmentValidatorInterface + */ + private $shipmentValidator; + + /** + * @var ValidatorResultMerger + */ + private $validatorResultMerger; + + /** + * ShipOrder constructor. + * + * @param OrderValidatorInterface $orderValidator + * @param ShipmentValidatorInterface $shipmentValidator + * @param ValidatorResultMerger $validatorResultMerger + */ + public function __construct( + OrderValidatorInterface $orderValidator, + ShipmentValidatorInterface $shipmentValidator, + ValidatorResultMerger $validatorResultMerger + ) { + $this->orderValidator = $orderValidator; + $this->shipmentValidator = $shipmentValidator; + $this->validatorResultMerger = $validatorResultMerger; + } + + /** + * @param OrderInterface $order + * @param ShipmentInterface $shipment + * @param array $items + * @param bool $notify + * @param bool $appendComment + * @param \Magento\Sales\Api\Data\ShipmentCommentCreationInterface|null $comment + * @param array $tracks + * @param array $packages + * @param \Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface|null $arguments + * @return \Magento\Sales\Model\ValidatorResultInterface + */ + public function validate( + $order, + $shipment, + array $items = [], + $notify = false, + $appendComment = false, + \Magento\Sales\Api\Data\ShipmentCommentCreationInterface $comment = null, + array $tracks = [], + array $packages = [], + \Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface $arguments = null + ) { + $orderValidationResult = $this->orderValidator->validate( + $order, + [ + CanShip::class + ] + ); + $shipmentValidationResult = $this->shipmentValidator->validate( + $shipment, + [ + QuantityValidator::class, + TrackValidator::class + ] + ); + + return $this->validatorResultMerger->merge($orderValidationResult, $shipmentValidationResult); + } +} diff --git a/app/code/Magento/Sales/Model/Order/Validation/ShipOrderInterface.php b/app/code/Magento/Sales/Model/Order/Validation/ShipOrderInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..43f12df445b799621d220182bc009ff02f74c1bb --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/Validation/ShipOrderInterface.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Model\Order\Validation; + +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\Data\ShipmentInterface; + +/** + * Interface ShipOrderInterface + * + * @api + */ +interface ShipOrderInterface +{ + /** + * @param OrderInterface $order + * @param ShipmentInterface $shipment + * @param array $items + * @param bool $notify + * @param bool $appendComment + * @param \Magento\Sales\Api\Data\ShipmentCommentCreationInterface|null $comment + * @param array $tracks + * @param array $packages + * @param \Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface|null $arguments + * @return \Magento\Sales\Model\ValidatorResultInterface + */ + public function validate( + $order, + $shipment, + array $items = [], + $notify = false, + $appendComment = false, + \Magento\Sales\Api\Data\ShipmentCommentCreationInterface $comment = null, + array $tracks = [], + array $packages = [], + \Magento\Sales\Api\Data\ShipmentCreationArgumentsInterface $arguments = null + ); +} diff --git a/app/code/Magento/Sales/Model/RefundInvoice.php b/app/code/Magento/Sales/Model/RefundInvoice.php index ad9f89b29bff8e18b579ac17c79da4286dc7aa84..60c3a2ac121254591cdc171433770d6c2d539e80 100644 --- a/app/code/Magento/Sales/Model/RefundInvoice.php +++ b/app/code/Magento/Sales/Model/RefundInvoice.php @@ -11,18 +11,11 @@ use Magento\Sales\Api\InvoiceRepositoryInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Api\RefundInvoiceInterface; use Magento\Sales\Model\Order\Config as OrderConfig; -use Magento\Sales\Model\Order\Creditmemo\CreditmemoValidatorInterface; -use Magento\Sales\Model\Order\Creditmemo\ItemCreationValidatorInterface; use Magento\Sales\Model\Order\Creditmemo\NotifierInterface; -use Magento\Sales\Model\Order\Creditmemo\Item\Validation\CreationQuantityValidator; -use Magento\Sales\Model\Order\Creditmemo\Validation\QuantityValidator; -use Magento\Sales\Model\Order\Creditmemo\Validation\TotalsValidator; use Magento\Sales\Model\Order\CreditmemoDocumentFactory; -use Magento\Sales\Model\Order\Invoice\InvoiceValidatorInterface; +use Magento\Sales\Model\Order\Validation\RefundInvoiceInterface as RefundInvoiceValidator; use Magento\Sales\Model\Order\OrderStateResolverInterface; -use Magento\Sales\Model\Order\OrderValidatorInterface; use Magento\Sales\Model\Order\PaymentAdapterInterface; -use Magento\Sales\Model\Order\Validation\CanRefund; use Psr\Log\LoggerInterface; /** @@ -51,33 +44,13 @@ class RefundInvoice implements RefundInvoiceInterface */ private $invoiceRepository; - /** - * @var OrderValidatorInterface - */ - private $orderValidator; - - /** - * @var InvoiceValidatorInterface - */ - private $invoiceValidator; - - /** - * @var CreditmemoValidatorInterface - */ - private $creditmemoValidator; - - /** - * @var ItemCreationValidatorInterface - */ - private $itemCreationValidator; - /** * @var CreditmemoRepositoryInterface */ private $creditmemoRepository; /** - * @var Order\PaymentAdapterInterface + * @var PaymentAdapterInterface */ private $paymentAdapter; @@ -87,7 +60,7 @@ class RefundInvoice implements RefundInvoiceInterface private $creditmemoDocumentFactory; /** - * @var Order\Creditmemo\NotifierInterface + * @var NotifierInterface */ private $notifier; @@ -101,6 +74,11 @@ class RefundInvoice implements RefundInvoiceInterface */ private $logger; + /** + * @var RefundInvoiceValidator + */ + private $validator; + /** * RefundInvoice constructor. * @@ -108,10 +86,7 @@ class RefundInvoice implements RefundInvoiceInterface * @param OrderStateResolverInterface $orderStateResolver * @param OrderRepositoryInterface $orderRepository * @param InvoiceRepositoryInterface $invoiceRepository - * @param OrderValidatorInterface $orderValidator - * @param InvoiceValidatorInterface $invoiceValidator - * @param CreditmemoValidatorInterface $creditmemoValidator - * @param Order\Creditmemo\ItemCreationValidatorInterface $itemCreationValidator + * @param RefundInvoiceValidator $validator * @param CreditmemoRepositoryInterface $creditmemoRepository * @param PaymentAdapterInterface $paymentAdapter * @param CreditmemoDocumentFactory $creditmemoDocumentFactory @@ -125,10 +100,7 @@ class RefundInvoice implements RefundInvoiceInterface OrderStateResolverInterface $orderStateResolver, OrderRepositoryInterface $orderRepository, InvoiceRepositoryInterface $invoiceRepository, - OrderValidatorInterface $orderValidator, - InvoiceValidatorInterface $invoiceValidator, - CreditmemoValidatorInterface $creditmemoValidator, - ItemCreationValidatorInterface $itemCreationValidator, + RefundInvoiceValidator $validator, CreditmemoRepositoryInterface $creditmemoRepository, PaymentAdapterInterface $paymentAdapter, CreditmemoDocumentFactory $creditmemoDocumentFactory, @@ -140,16 +112,13 @@ class RefundInvoice implements RefundInvoiceInterface $this->orderStateResolver = $orderStateResolver; $this->orderRepository = $orderRepository; $this->invoiceRepository = $invoiceRepository; - $this->orderValidator = $orderValidator; - $this->creditmemoValidator = $creditmemoValidator; - $this->itemCreationValidator = $itemCreationValidator; + $this->validator = $validator; $this->creditmemoRepository = $creditmemoRepository; $this->paymentAdapter = $paymentAdapter; $this->creditmemoDocumentFactory = $creditmemoDocumentFactory; $this->notifier = $notifier; $this->config = $config; $this->logger = $logger; - $this->invoiceValidator = $invoiceValidator; } /** @@ -174,45 +143,21 @@ class RefundInvoice implements RefundInvoiceInterface ($appendComment && $notify), $arguments ); - $orderValidationResult = $this->orderValidator->validate( - $order, - [ - CanRefund::class - ] - ); - $invoiceValidationResult = $this->invoiceValidator->validate( + + $validationMessages = $this->validator->validate( $invoice, - [ - \Magento\Sales\Model\Order\Invoice\Validation\CanRefund::class - ] - ); - $creditmemoValidationResult = $this->creditmemoValidator->validate( + $order, $creditmemo, - [ - QuantityValidator::class, - TotalsValidator::class - ] - ); - $itemsValidation = []; - foreach ($items as $item) { - $itemsValidation = array_merge( - $itemsValidation, - $this->itemCreationValidator->validate( - $item, - [CreationQuantityValidator::class], - $order - ) - ); - } - $validationMessages = array_merge( - $orderValidationResult, - $invoiceValidationResult, - $creditmemoValidationResult, - $itemsValidation + $items, + $isOnline, + $notify, + $appendComment, + $comment, + $arguments ); - if (!empty($validationMessages )) { + if ($validationMessages->hasMessages()) { throw new \Magento\Sales\Exception\DocumentValidationException( - __("Creditmemo Document Validation Error(s):\n" . implode("\n", $validationMessages)) + __("Creditmemo Document Validation Error(s):\n" . implode("\n", $validationMessages->getMessages())) ); } $connection->beginTransaction(); diff --git a/app/code/Magento/Sales/Model/RefundOrder.php b/app/code/Magento/Sales/Model/RefundOrder.php index 4cfbe063ea81f29e7a6608c0c04fec269326c44c..abd6e25416729b3b995ba92425cbc3c3c7de8943 100644 --- a/app/code/Magento/Sales/Model/RefundOrder.php +++ b/app/code/Magento/Sales/Model/RefundOrder.php @@ -10,17 +10,11 @@ use Magento\Sales\Api\CreditmemoRepositoryInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Api\RefundOrderInterface; use Magento\Sales\Model\Order\Config as OrderConfig; -use Magento\Sales\Model\Order\Creditmemo\CreditmemoValidatorInterface; -use Magento\Sales\Model\Order\Creditmemo\ItemCreationValidatorInterface; use Magento\Sales\Model\Order\Creditmemo\NotifierInterface; -use Magento\Sales\Model\Order\Creditmemo\Item\Validation\CreationQuantityValidator; -use Magento\Sales\Model\Order\Creditmemo\Validation\QuantityValidator; -use Magento\Sales\Model\Order\Creditmemo\Validation\TotalsValidator; use Magento\Sales\Model\Order\CreditmemoDocumentFactory; use Magento\Sales\Model\Order\OrderStateResolverInterface; -use Magento\Sales\Model\Order\OrderValidatorInterface; use Magento\Sales\Model\Order\PaymentAdapterInterface; -use Magento\Sales\Model\Order\Validation\CanRefund; +use Magento\Sales\Model\Order\Validation\RefundOrderInterface as RefundOrderValidator; use Psr\Log\LoggerInterface; /** @@ -44,28 +38,13 @@ class RefundOrder implements RefundOrderInterface */ private $orderRepository; - /** - * @var OrderValidatorInterface - */ - private $orderValidator; - - /** - * @var CreditmemoValidatorInterface - */ - private $creditmemoValidator; - - /** - * @var Order\Creditmemo\ItemCreationValidatorInterface - */ - private $itemCreationValidator; - /** * @var CreditmemoRepositoryInterface */ private $creditmemoRepository; /** - * @var Order\PaymentAdapterInterface + * @var PaymentAdapterInterface */ private $paymentAdapter; @@ -75,7 +54,12 @@ class RefundOrder implements RefundOrderInterface private $creditmemoDocumentFactory; /** - * @var Order\Creditmemo\NotifierInterface + * @var RefundOrderValidator + */ + private $validator; + + /** + * @var NotifierInterface */ private $notifier; @@ -91,15 +75,14 @@ class RefundOrder implements RefundOrderInterface /** * RefundOrder constructor. + * * @param ResourceConnection $resourceConnection * @param OrderStateResolverInterface $orderStateResolver * @param OrderRepositoryInterface $orderRepository - * @param OrderValidatorInterface $orderValidator - * @param CreditmemoValidatorInterface $creditmemoValidator - * @param ItemCreationValidatorInterface $itemCreationValidator * @param CreditmemoRepositoryInterface $creditmemoRepository * @param PaymentAdapterInterface $paymentAdapter * @param CreditmemoDocumentFactory $creditmemoDocumentFactory + * @param RefundOrderValidator $validator * @param NotifierInterface $notifier * @param OrderConfig $config * @param LoggerInterface $logger @@ -109,12 +92,10 @@ class RefundOrder implements RefundOrderInterface ResourceConnection $resourceConnection, OrderStateResolverInterface $orderStateResolver, OrderRepositoryInterface $orderRepository, - OrderValidatorInterface $orderValidator, - CreditmemoValidatorInterface $creditmemoValidator, - ItemCreationValidatorInterface $itemCreationValidator, CreditmemoRepositoryInterface $creditmemoRepository, PaymentAdapterInterface $paymentAdapter, CreditmemoDocumentFactory $creditmemoDocumentFactory, + RefundOrderValidator $validator, NotifierInterface $notifier, OrderConfig $config, LoggerInterface $logger @@ -122,12 +103,10 @@ class RefundOrder implements RefundOrderInterface $this->resourceConnection = $resourceConnection; $this->orderStateResolver = $orderStateResolver; $this->orderRepository = $orderRepository; - $this->orderValidator = $orderValidator; - $this->creditmemoValidator = $creditmemoValidator; - $this->itemCreationValidator = $itemCreationValidator; $this->creditmemoRepository = $creditmemoRepository; $this->paymentAdapter = $paymentAdapter; $this->creditmemoDocumentFactory = $creditmemoDocumentFactory; + $this->validator = $validator; $this->notifier = $notifier; $this->config = $config; $this->logger = $logger; @@ -153,34 +132,18 @@ class RefundOrder implements RefundOrderInterface ($appendComment && $notify), $arguments ); - $orderValidationResult = $this->orderValidator->validate( + $validationMessages = $this->validator->validate( $order, - [ - CanRefund::class - ] - ); - $creditmemoValidationResult = $this->creditmemoValidator->validate( $creditmemo, - [ - QuantityValidator::class, - TotalsValidator::class - ] + $items, + $notify, + $appendComment, + $comment, + $arguments ); - $itemsValidation = []; - foreach ($items as $item) { - $itemsValidation = array_merge( - $itemsValidation, - $this->itemCreationValidator->validate( - $item, - [CreationQuantityValidator::class], - $order - ) - ); - } - $validationMessages = array_merge($orderValidationResult, $creditmemoValidationResult, $itemsValidation); - if (!empty($validationMessages)) { + if ($validationMessages->hasMessages()) { throw new \Magento\Sales\Exception\DocumentValidationException( - __("Creditmemo Document Validation Error(s):\n" . implode("\n", $validationMessages)) + __("Creditmemo Document Validation Error(s):\n" . implode("\n", $validationMessages->getMessages())) ); } $connection->beginTransaction(); diff --git a/app/code/Magento/Sales/Model/ShipOrder.php b/app/code/Magento/Sales/Model/ShipOrder.php index d051144cf73ca166c30ebd6eb9f626306b1720a6..034442a19c1f7e297e364e894ef53275574c4706 100644 --- a/app/code/Magento/Sales/Model/ShipOrder.php +++ b/app/code/Magento/Sales/Model/ShipOrder.php @@ -11,14 +11,10 @@ use Magento\Sales\Api\ShipmentRepositoryInterface; use Magento\Sales\Api\ShipOrderInterface; use Magento\Sales\Model\Order\Config as OrderConfig; use Magento\Sales\Model\Order\OrderStateResolverInterface; -use Magento\Sales\Model\Order\OrderValidatorInterface; use Magento\Sales\Model\Order\ShipmentDocumentFactory; use Magento\Sales\Model\Order\Shipment\NotifierInterface; -use Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface; -use Magento\Sales\Model\Order\Shipment\Validation\QuantityValidator; -use Magento\Sales\Model\Order\Shipment\Validation\TrackValidator; -use Magento\Sales\Model\Order\Validation\CanShip; use Magento\Sales\Model\Order\Shipment\OrderRegistrarInterface; +use Magento\Sales\Model\Order\Validation\ShipOrderInterface as ShipOrderValidator; use Psr\Log\LoggerInterface; /** @@ -42,11 +38,6 @@ class ShipOrder implements ShipOrderInterface */ private $shipmentDocumentFactory; - /** - * @var ShipmentValidatorInterface - */ - private $shipmentValidator; - /** * @var OrderStateResolverInterface */ @@ -62,6 +53,11 @@ class ShipOrder implements ShipOrderInterface */ private $shipmentRepository; + /** + * @var ShipOrderValidator + */ + private $shipOrderValidator; + /** * @var NotifierInterface */ @@ -72,11 +68,6 @@ class ShipOrder implements ShipOrderInterface */ private $logger; - /** - * @var OrderValidatorInterface - */ - private $orderValidator; - /** * @var OrderRegistrarInterface */ @@ -86,11 +77,10 @@ class ShipOrder implements ShipOrderInterface * @param ResourceConnection $resourceConnection * @param OrderRepositoryInterface $orderRepository * @param ShipmentDocumentFactory $shipmentDocumentFactory - * @param ShipmentValidatorInterface $shipmentValidator - * @param OrderValidatorInterface $orderValidator * @param OrderStateResolverInterface $orderStateResolver * @param OrderConfig $config * @param ShipmentRepositoryInterface $shipmentRepository + * @param ShipOrderValidator $shipOrderValidator * @param NotifierInterface $notifierInterface * @param OrderRegistrarInterface $orderRegistrar * @param LoggerInterface $logger @@ -100,11 +90,10 @@ class ShipOrder implements ShipOrderInterface ResourceConnection $resourceConnection, OrderRepositoryInterface $orderRepository, ShipmentDocumentFactory $shipmentDocumentFactory, - ShipmentValidatorInterface $shipmentValidator, - OrderValidatorInterface $orderValidator, OrderStateResolverInterface $orderStateResolver, OrderConfig $config, ShipmentRepositoryInterface $shipmentRepository, + ShipOrderValidator $shipOrderValidator, NotifierInterface $notifierInterface, OrderRegistrarInterface $orderRegistrar, LoggerInterface $logger @@ -112,11 +101,10 @@ class ShipOrder implements ShipOrderInterface $this->resourceConnection = $resourceConnection; $this->orderRepository = $orderRepository; $this->shipmentDocumentFactory = $shipmentDocumentFactory; - $this->shipmentValidator = $shipmentValidator; - $this->orderValidator = $orderValidator; $this->orderStateResolver = $orderStateResolver; $this->config = $config; $this->shipmentRepository = $shipmentRepository; + $this->shipOrderValidator = $shipOrderValidator; $this->notifierInterface = $notifierInterface; $this->logger = $logger; $this->orderRegistrar = $orderRegistrar; @@ -159,23 +147,19 @@ class ShipOrder implements ShipOrderInterface $packages, $arguments ); - $orderValidationResult = $this->orderValidator->validate( + $validationMessages = $this->shipOrderValidator->validate( $order, - [ - CanShip::class - ] - ); - $shipmentValidationResult = $this->shipmentValidator->validate( $shipment, - [ - QuantityValidator::class, - TrackValidator::class - ] + $items, + $notify, + $appendComment, + $comment, + $tracks, + $packages ); - $validationMessages = array_merge($orderValidationResult, $shipmentValidationResult); - if (!empty($validationMessages)) { + if ($validationMessages->hasMessages()) { throw new \Magento\Sales\Exception\DocumentValidationException( - __("Shipment Document Validation Error(s):\n" . implode("\n", $validationMessages)) + __("Shipment Document Validation Error(s):\n" . implode("\n", $validationMessages->getMessages())) ); } $connection->beginTransaction(); diff --git a/app/code/Magento/Sales/Model/Validator.php b/app/code/Magento/Sales/Model/Validator.php index 10aa0735b952e99e0d889b27ff378411d0d1d6d1..9239223fe3b4a84ee1e319e6f067ca9ba6cfb3e8 100644 --- a/app/code/Magento/Sales/Model/Validator.php +++ b/app/code/Magento/Sales/Model/Validator.php @@ -20,21 +20,30 @@ class Validator */ private $objectManager; + /** + * @var ValidatorResultInterfaceFactory + */ + private $validatorResultFactory; + /** * Validator constructor. * * @param ObjectManagerInterface $objectManager + * @param ValidatorResultInterfaceFactory $validatorResult */ - public function __construct(ObjectManagerInterface $objectManager) - { + public function __construct( + ObjectManagerInterface $objectManager, + ValidatorResultInterfaceFactory $validatorResult + ) { $this->objectManager = $objectManager; + $this->validatorResultFactory = $validatorResult; } /** * @param object $entity * @param ValidatorInterface[] $validators * @param object|null $context - * @return \string[] + * @return ValidatorResultInterface * @throws ConfigurationMismatchException */ public function validate($entity, array $validators, $context = null) @@ -56,7 +65,11 @@ class Validator } $messages = array_merge($messages, $validator->validate($entity)); } + $validationResult = $this->validatorResultFactory->create(); + foreach ($messages as $message) { + $validationResult->addMessage($message); + } - return $messages; + return $validationResult; } } diff --git a/app/code/Magento/Sales/Model/ValidatorResult.php b/app/code/Magento/Sales/Model/ValidatorResult.php new file mode 100644 index 0000000000000000000000000000000000000000..6dc2933ac42a7c2974d397aebf3e3c599831b5bf --- /dev/null +++ b/app/code/Magento/Sales/Model/ValidatorResult.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Model; + +/** + * Class ValidatorResult + */ +class ValidatorResult implements ValidatorResultInterface +{ + /** + * @var \string[] + */ + private $messages = []; + + /** + * @inheritdoc + */ + public function addMessage($message) + { + $this->messages[] = (string)$message; + } + + /** + * @return bool + */ + public function hasMessages() + { + return count($this->messages) > 0; + } + + /** + * @return \string[] + */ + public function getMessages() + { + return $this->messages; + } +} diff --git a/app/code/Magento/Sales/Model/ValidatorResultInterface.php b/app/code/Magento/Sales/Model/ValidatorResultInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..c4e284657bdd1f1261094f05337577ebf3656ad7 --- /dev/null +++ b/app/code/Magento/Sales/Model/ValidatorResultInterface.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Model; + +/** + * Interface ValidatorResultInterface + * @api + */ +interface ValidatorResultInterface +{ + /** + * @param string $message + * @return void + */ + public function addMessage($message); + + /** + * @return bool + */ + public function hasMessages(); + + /** + * @return \string[] + */ + public function getMessages(); +} diff --git a/app/code/Magento/Sales/Model/ValidatorResultMerger.php b/app/code/Magento/Sales/Model/ValidatorResultMerger.php new file mode 100644 index 0000000000000000000000000000000000000000..41f38e8eef078b221c70cdb9520f6043ccfe4daf --- /dev/null +++ b/app/code/Magento/Sales/Model/ValidatorResultMerger.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Sales\Model; + +/** + * Class ValidatorResultMerger + */ +class ValidatorResultMerger +{ + /** + * @var ValidatorResultInterfaceFactory + */ + private $validatorResultInterfaceFactory; + + /** + * ValidatorResultMerger constructor. + * + * @param ValidatorResultInterfaceFactory $validatorResultInterfaceFactory + */ + public function __construct(ValidatorResultInterfaceFactory $validatorResultInterfaceFactory) + { + $this->validatorResultInterfaceFactory = $validatorResultInterfaceFactory; + } + + /** + * Merge two validator results and additional messages + * + * @param ValidatorResultInterface $first + * @param ValidatorResultInterface $second + * @param \string[] $validatorMessages + * @return ValidatorResultInterface + */ + public function merge(ValidatorResultInterface $first, ValidatorResultInterface $second, ... $validatorMessages) + { + $messages = array_merge($first->getMessages(), $second->getMessages(), ...$validatorMessages); + + $result = $this->validatorResultInterfaceFactory->create(); + foreach ($messages as $message) { + $result->addMessage($message); + } + + return $result; + } +} diff --git a/app/code/Magento/Sales/Test/Unit/Model/InvoiceOrderTest.php b/app/code/Magento/Sales/Test/Unit/Model/InvoiceOrderTest.php index 6dfa929acb6290e0e95bf2011b76ec9811691ecc..1169e230e75421d150a2719edfb14c09fddf1120 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/InvoiceOrderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/InvoiceOrderTest.php @@ -14,19 +14,19 @@ use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Config as OrderConfig; -use Magento\Sales\Model\Order\Invoice\InvoiceValidatorInterface; use Magento\Sales\Model\Order\Invoice\NotifierInterface; use Magento\Sales\Model\Order\InvoiceDocumentFactory; use Magento\Sales\Model\Order\InvoiceRepository; use Magento\Sales\Model\Order\OrderStateResolverInterface; -use Magento\Sales\Model\Order\OrderValidatorInterface; +use Magento\Sales\Model\Order\Validation\InvoiceOrderInterface; use Magento\Sales\Model\Order\PaymentAdapterInterface; +use Magento\Sales\Model\ValidatorResultInterface; use Magento\Sales\Model\InvoiceOrder; use Psr\Log\LoggerInterface; /** * Class InvoiceOrderTest - * + * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -48,14 +48,9 @@ class InvoiceOrderTest extends \PHPUnit_Framework_TestCase private $invoiceDocumentFactoryMock; /** - * @var InvoiceValidatorInterface|\PHPUnit_Framework_MockObject_MockObject + * @var InvoiceOrderInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $invoiceValidatorMock; - - /** - * @var OrderValidatorInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $orderValidatorMock; + private $invoiceOrderValidatorMock; /** * @var PaymentAdapterInterface|\PHPUnit_Framework_MockObject_MockObject @@ -117,6 +112,11 @@ class InvoiceOrderTest extends \PHPUnit_Framework_TestCase */ private $loggerMock; + /** + * @var ValidatorResultInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $errorMessagesMock; + protected function setUp() { $this->resourceConnectionMock = $this->getMockBuilder(ResourceConnection::class) @@ -131,14 +131,6 @@ class InvoiceOrderTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); - $this->invoiceValidatorMock = $this->getMockBuilder(InvoiceValidatorInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->orderValidatorMock = $this->getMockBuilder(OrderValidatorInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->paymentAdapterMock = $this->getMockBuilder(PaymentAdapterInterface::class) ->disableOriginalConstructor() ->getMock(); @@ -183,22 +175,37 @@ class InvoiceOrderTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); + $this->invoiceOrderValidatorMock = $this->getMockBuilder(InvoiceOrderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->errorMessagesMock = $this->getMockBuilder(ValidatorResultInterface::class) + ->disableOriginalConstructor() + ->setMethods(['hasMessages', 'getMessages', 'addMessage']) + ->getMock(); + $this->invoiceOrder = new InvoiceOrder( $this->resourceConnectionMock, $this->orderRepositoryMock, $this->invoiceDocumentFactoryMock, - $this->invoiceValidatorMock, - $this->orderValidatorMock, $this->paymentAdapterMock, $this->orderStateResolverMock, $this->configMock, $this->invoiceRepositoryMock, + $this->invoiceOrderValidatorMock, $this->notifierInterfaceMock, $this->loggerMock ); } /** + * @param int $orderId + * @param bool $capture + * @param array $items + * @param bool $notify + * @param bool $appendComment + * @throws \Magento\Sales\Exception\CouldNotInvoiceException + * @throws \Magento\Sales\Exception\DocumentValidationException * @dataProvider dataProvider */ public function testOrderInvoice($orderId, $capture, $items, $notify, $appendComment) @@ -207,11 +214,9 @@ class InvoiceOrderTest extends \PHPUnit_Framework_TestCase ->method('getConnection') ->with('sales') ->willReturn($this->adapterInterface); - $this->orderRepositoryMock->expects($this->once()) ->method('get') ->willReturn($this->orderMock); - $this->invoiceDocumentFactoryMock->expects($this->once()) ->method('create') ->with( @@ -221,66 +226,62 @@ class InvoiceOrderTest extends \PHPUnit_Framework_TestCase ($appendComment && $notify), $this->invoiceCreationArgumentsMock )->willReturn($this->invoiceMock); - - $this->invoiceValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->invoiceMock) - ->willReturn([]); - $this->orderValidatorMock->expects($this->once()) + $this->invoiceOrderValidatorMock->expects($this->once()) ->method('validate') - ->with($this->orderMock) - ->willReturn([]); - + ->with( + $this->orderMock, + $this->invoiceMock, + $capture, + $items, + $notify, + $appendComment, + $this->invoiceCommentCreationMock, + $this->invoiceCreationArgumentsMock + ) + ->willReturn($this->errorMessagesMock); + $hasMessages = false; + $this->errorMessagesMock->expects($this->once()) + ->method('hasMessages')->willReturn($hasMessages); $this->paymentAdapterMock->expects($this->once()) ->method('pay') ->with($this->orderMock, $this->invoiceMock, $capture) ->willReturn($this->orderMock); - $this->orderStateResolverMock->expects($this->once()) ->method('getStateForOrder') ->with($this->orderMock, [OrderStateResolverInterface::IN_PROGRESS]) ->willReturn(Order::STATE_PROCESSING); - $this->orderMock->expects($this->once()) ->method('setState') ->with(Order::STATE_PROCESSING) ->willReturnSelf(); - $this->orderMock->expects($this->once()) ->method('getState') ->willReturn(Order::STATE_PROCESSING); - $this->configMock->expects($this->once()) ->method('getStateDefaultStatus') ->with(Order::STATE_PROCESSING) ->willReturn('Processing'); - $this->orderMock->expects($this->once()) ->method('setStatus') ->with('Processing') ->willReturnSelf(); - $this->invoiceMock->expects($this->once()) ->method('setState') ->with(\Magento\Sales\Model\Order\Invoice::STATE_PAID) ->willReturnSelf(); - $this->invoiceRepositoryMock->expects($this->once()) ->method('save') ->with($this->invoiceMock) ->willReturn($this->invoiceMock); - $this->orderRepositoryMock->expects($this->once()) ->method('save') ->with($this->orderMock) ->willReturn($this->orderMock); - if ($notify) { $this->notifierInterfaceMock->expects($this->once()) ->method('notify') ->with($this->orderMock, $this->invoiceMock, $this->invoiceCommentCreationMock); } - $this->invoiceMock->expects($this->once()) ->method('getEntityId') ->willReturn(2); @@ -325,14 +326,25 @@ class InvoiceOrderTest extends \PHPUnit_Framework_TestCase $this->invoiceCreationArgumentsMock )->willReturn($this->invoiceMock); - $this->invoiceValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->invoiceMock) - ->willReturn($errorMessages); - $this->orderValidatorMock->expects($this->once()) + $this->invoiceOrderValidatorMock->expects($this->once()) ->method('validate') - ->with($this->orderMock) - ->willReturn([]); + ->with( + $this->orderMock, + $this->invoiceMock, + $capture, + $items, + $notify, + $appendComment, + $this->invoiceCommentCreationMock, + $this->invoiceCreationArgumentsMock + ) + ->willReturn($this->errorMessagesMock); + $hasMessages = true; + + $this->errorMessagesMock->expects($this->once()) + ->method('hasMessages')->willReturn($hasMessages); + $this->errorMessagesMock->expects($this->once()) + ->method('getMessages')->willReturn($errorMessages); $this->invoiceOrder->execute( $orderId, @@ -374,16 +386,25 @@ class InvoiceOrderTest extends \PHPUnit_Framework_TestCase $this->invoiceCreationArgumentsMock )->willReturn($this->invoiceMock); - $this->invoiceValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->invoiceMock) - ->willReturn([]); - $this->orderValidatorMock->expects($this->once()) + $this->invoiceOrderValidatorMock->expects($this->once()) ->method('validate') - ->with($this->orderMock) - ->willReturn([]); - $e = new \Exception(); + ->with( + $this->orderMock, + $this->invoiceMock, + $capture, + $items, + $notify, + $appendComment, + $this->invoiceCommentCreationMock, + $this->invoiceCreationArgumentsMock + ) + ->willReturn($this->errorMessagesMock); + $hasMessages = false; + $this->errorMessagesMock->expects($this->once()) + ->method('hasMessages')->willReturn($hasMessages); + + $e = new \Exception(); $this->paymentAdapterMock->expects($this->once()) ->method('pay') ->with($this->orderMock, $this->invoiceMock, $capture) diff --git a/app/code/Magento/Sales/Test/Unit/Model/RefundInvoiceTest.php b/app/code/Magento/Sales/Test/Unit/Model/RefundInvoiceTest.php index d400c7e1c0d4ed4bd38e6ff7a4eb9a5cb460f833..d249f039a140b69e0aeebf444aff1f3961f0c0b2 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/RefundInvoiceTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/RefundInvoiceTest.php @@ -17,15 +17,13 @@ use Magento\Sales\Api\InvoiceRepositoryInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Config as OrderConfig; -use Magento\Sales\Model\Order\Creditmemo\CreditmemoValidatorInterface; -use Magento\Sales\Model\Order\Creditmemo\ItemCreationValidatorInterface; use Magento\Sales\Api\Data\CreditmemoItemCreationInterface; use Magento\Sales\Model\Order\CreditmemoDocumentFactory; -use Magento\Sales\Model\Order\Invoice\InvoiceValidatorInterface; use Magento\Sales\Model\Order\OrderStateResolverInterface; -use Magento\Sales\Model\Order\OrderValidatorInterface; use Magento\Sales\Model\Order\PaymentAdapterInterface; use Magento\Sales\Model\Order\Creditmemo\NotifierInterface; +use Magento\Sales\Model\Order\Validation\RefundInvoiceInterface; +use Magento\Sales\Model\ValidatorResultInterface; use Magento\Sales\Model\RefundInvoice; use Psr\Log\LoggerInterface; @@ -56,21 +54,6 @@ class RefundInvoiceTest extends \PHPUnit_Framework_TestCase */ private $creditmemoDocumentFactoryMock; - /** - * @var CreditmemoValidatorInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $creditmemoValidatorMock; - - /** - * @var OrderValidatorInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $orderValidatorMock; - - /** - * @var InvoiceValidatorInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $invoiceValidatorMock; - /** * @var PaymentAdapterInterface|\PHPUnit_Framework_MockObject_MockObject */ @@ -137,9 +120,14 @@ class RefundInvoiceTest extends \PHPUnit_Framework_TestCase private $creditmemoItemCreationMock; /** - * @var ItemCreationValidatorInterface|\PHPUnit_Framework_MockObject_MockObject + * @var RefundInvoiceInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $itemCreationValidatorMock; + private $refundInvoiceValidatorMock; + + /** + * @var ValidatorResultInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $validationMessagesMock; /** * @var LoggerInterface|\PHPUnit_Framework_MockObject_MockObject @@ -160,24 +148,15 @@ class RefundInvoiceTest extends \PHPUnit_Framework_TestCase $this->creditmemoDocumentFactoryMock = $this->getMockBuilder(CreditmemoDocumentFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->creditmemoValidatorMock = $this->getMockBuilder(CreditmemoValidatorInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->orderValidatorMock = $this->getMockBuilder(OrderValidatorInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->invoiceValidatorMock = $this->getMockBuilder(InvoiceValidatorInterface::class) + $this->paymentAdapterMock = $this->getMockBuilder(PaymentAdapterInterface::class) ->disableOriginalConstructor() ->getMock(); - - $this->paymentAdapterMock = $this->getMockBuilder(PaymentAdapterInterface::class) + $this->refundInvoiceValidatorMock = $this->getMockBuilder(RefundInvoiceInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->orderStateResolverMock = $this->getMockBuilder(OrderStateResolverInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->configMock = $this->getMockBuilder(OrderConfig::class) ->disableOriginalConstructor() ->getMock(); @@ -221,20 +200,17 @@ class RefundInvoiceTest extends \PHPUnit_Framework_TestCase $this->creditmemoItemCreationMock = $this->getMockBuilder(CreditmemoItemCreationInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - - $this->itemCreationValidatorMock = $this->getMockBuilder(ItemCreationValidatorInterface::class) + $this->validationMessagesMock = $this->getMockBuilder(ValidatorResultInterface::class) ->disableOriginalConstructor() - ->getMockForAbstractClass(); + ->setMethods(['hasMessages', 'getMessages', 'addMessage']) + ->getMock(); $this->refundInvoice = new RefundInvoice( $this->resourceConnectionMock, $this->orderStateResolverMock, $this->orderRepositoryMock, $this->invoiceRepositoryMock, - $this->orderValidatorMock, - $this->invoiceValidatorMock, - $this->creditmemoValidatorMock, - $this->itemCreationValidatorMock, + $this->refundInvoiceValidatorMock, $this->creditmemoRepositoryMock, $this->paymentAdapterMock, $this->creditmemoDocumentFactoryMock, @@ -245,22 +221,27 @@ class RefundInvoiceTest extends \PHPUnit_Framework_TestCase } /** + * @param int $invoiceId + * @param bool $isOnline + * @param array $items + * @param bool $notify + * @param bool $appendComment + * @throws \Magento\Sales\Exception\CouldNotRefundException + * @throws \Magento\Sales\Exception\DocumentValidationException * @dataProvider dataProvider */ - public function testOrderCreditmemo($invoiceId, $items, $notify, $appendComment) + public function testOrderCreditmemo($invoiceId, $isOnline, $items, $notify, $appendComment) { $this->resourceConnectionMock->expects($this->once()) ->method('getConnection') ->with('sales') ->willReturn($this->adapterInterface); - $this->invoiceRepositoryMock->expects($this->once()) ->method('get') ->willReturn($this->invoiceMock); $this->orderRepositoryMock->expects($this->once()) ->method('get') ->willReturn($this->orderMock); - $this->creditmemoDocumentFactoryMock->expects($this->once()) ->method('createFromInvoice') ->with( @@ -270,24 +251,23 @@ class RefundInvoiceTest extends \PHPUnit_Framework_TestCase ($appendComment && $notify), $this->creditmemoCreationArgumentsMock )->willReturn($this->creditmemoMock); - - $this->creditmemoValidatorMock->expects($this->once()) + $this->refundInvoiceValidatorMock->expects($this->once()) ->method('validate') - ->with($this->creditmemoMock) - ->willReturn([]); - $this->orderValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->orderMock) - ->willReturn([]); - $this->invoiceValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->invoiceMock) - ->willReturn([]); - $this->itemCreationValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->creditmemoItemCreationMock) - ->willReturn([]); - $this->orderMock->expects($this->once())->method('setCustomerNoteNotify')->with($notify); + ->with( + $this->invoiceMock, + $this->orderMock, + $this->creditmemoMock, + $items, + $isOnline, + $notify, + $appendComment, + $this->creditmemoCommentCreationMock, + $this->creditmemoCreationArgumentsMock + ) + ->willReturn($this->validationMessagesMock); + $hasMessages = false; + $this->validationMessagesMock->expects($this->once()) + ->method('hasMessages')->willReturn($hasMessages); $this->paymentAdapterMock->expects($this->once()) ->method('refund') ->with($this->creditmemoMock, $this->orderMock) @@ -315,7 +295,6 @@ class RefundInvoiceTest extends \PHPUnit_Framework_TestCase ->method('setState') ->with(\Magento\Sales\Model\Order\Creditmemo::STATE_REFUNDED) ->willReturnSelf(); - $this->creditmemoRepositoryMock->expects($this->once()) ->method('save') ->with($this->creditmemoMock) @@ -338,7 +317,7 @@ class RefundInvoiceTest extends \PHPUnit_Framework_TestCase $this->refundInvoice->execute( $invoiceId, $items, - false, + true, $notify, $appendComment, $this->creditmemoCommentCreationMock, @@ -356,6 +335,7 @@ class RefundInvoiceTest extends \PHPUnit_Framework_TestCase $items = [1 => $this->creditmemoItemCreationMock]; $notify = true; $appendComment = true; + $isOnline = false; $errorMessages = ['error1', 'error2']; $this->invoiceRepositoryMock->expects($this->once()) @@ -375,22 +355,25 @@ class RefundInvoiceTest extends \PHPUnit_Framework_TestCase $this->creditmemoCreationArgumentsMock )->willReturn($this->creditmemoMock); - $this->creditmemoValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->creditmemoMock) - ->willReturn($errorMessages); - $this->orderValidatorMock->expects($this->once()) + $this->refundInvoiceValidatorMock->expects($this->once()) ->method('validate') - ->with($this->orderMock) - ->willReturn([]); - $this->invoiceValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->invoiceMock) - ->willReturn([]); - $this->itemCreationValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->creditmemoItemCreationMock) - ->willReturn([]); + ->with( + $this->invoiceMock, + $this->orderMock, + $this->creditmemoMock, + $items, + $isOnline, + $notify, + $appendComment, + $this->creditmemoCommentCreationMock, + $this->creditmemoCreationArgumentsMock + ) + ->willReturn($this->validationMessagesMock); + $hasMessages = true; + $this->validationMessagesMock->expects($this->once()) + ->method('hasMessages')->willReturn($hasMessages); + $this->validationMessagesMock->expects($this->once()) + ->method('getMessages')->willReturn($errorMessages); $this->assertEquals( $errorMessages, @@ -415,6 +398,7 @@ class RefundInvoiceTest extends \PHPUnit_Framework_TestCase $items = [1 => $this->creditmemoItemCreationMock]; $notify = true; $appendComment = true; + $isOnline = false; $this->resourceConnectionMock->expects($this->once()) ->method('getConnection') ->with('sales') @@ -437,22 +421,23 @@ class RefundInvoiceTest extends \PHPUnit_Framework_TestCase $this->creditmemoCreationArgumentsMock )->willReturn($this->creditmemoMock); - $this->creditmemoValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->creditmemoMock) - ->willReturn([]); - $this->orderValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->orderMock) - ->willReturn([]); - $this->invoiceValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->invoiceMock) - ->willReturn([]); - $this->itemCreationValidatorMock->expects($this->once()) + $this->refundInvoiceValidatorMock->expects($this->once()) ->method('validate') - ->with($this->creditmemoItemCreationMock) - ->willReturn([]); + ->with( + $this->invoiceMock, + $this->orderMock, + $this->creditmemoMock, + $items, + $isOnline, + $notify, + $appendComment, + $this->creditmemoCommentCreationMock, + $this->creditmemoCreationArgumentsMock + ) + ->willReturn($this->validationMessagesMock); + $hasMessages = false; + $this->validationMessagesMock->expects($this->once()) + ->method('hasMessages')->willReturn($hasMessages); $e = new \Exception(); $this->paymentAdapterMock->expects($this->once()) @@ -485,8 +470,8 @@ class RefundInvoiceTest extends \PHPUnit_Framework_TestCase ->getMockForAbstractClass(); return [ - 'TestWithNotifyTrue' => [1, [1 => $creditmemoItemCreationMock], true, true], - 'TestWithNotifyFalse' => [1, [1 => $creditmemoItemCreationMock], false, true], + 'TestWithNotifyTrue' => [1, true, [1 => $creditmemoItemCreationMock], true, true], + 'TestWithNotifyFalse' => [1, true, [1 => $creditmemoItemCreationMock], false, true], ]; } } diff --git a/app/code/Magento/Sales/Test/Unit/Model/RefundOrderTest.php b/app/code/Magento/Sales/Test/Unit/Model/RefundOrderTest.php index 7cc2673981c03d9fe858b38f10f7de16cd64287a..4b61453e3c6a2a9cc996dce60a52714fe0113cc2 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/RefundOrderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/RefundOrderTest.php @@ -15,14 +15,13 @@ use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Config as OrderConfig; -use Magento\Sales\Model\Order\Creditmemo\CreditmemoValidatorInterface; -use Magento\Sales\Model\Order\Creditmemo\Item\Validation\CreationQuantityValidator; use Magento\Sales\Model\Order\CreditmemoDocumentFactory; use Magento\Sales\Model\Order\OrderStateResolverInterface; -use Magento\Sales\Model\Order\OrderValidatorInterface; +use Magento\Sales\Model\Order\Validation\RefundOrderInterface; use Magento\Sales\Model\Order\PaymentAdapterInterface; use Magento\Sales\Model\Order\Creditmemo\NotifierInterface; use Magento\Sales\Model\RefundOrder; +use Magento\Sales\Model\ValidatorResultInterface; use Psr\Log\LoggerInterface; use Magento\Sales\Api\Data\CreditmemoItemCreationInterface; @@ -48,16 +47,6 @@ class RefundOrderTest extends \PHPUnit_Framework_TestCase */ private $creditmemoDocumentFactoryMock; - /** - * @var CreditmemoValidatorInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $creditmemoValidatorMock; - - /** - * @var OrderValidatorInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $orderValidatorMock; - /** * @var PaymentAdapterInterface|\PHPUnit_Framework_MockObject_MockObject */ @@ -119,15 +108,20 @@ class RefundOrderTest extends \PHPUnit_Framework_TestCase private $loggerMock; /** - * @var Order\Creditmemo\ItemCreationValidatorInterface|\PHPUnit_Framework_MockObject_MockObject + * @var RefundOrderInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $itemCreationValidatorMock; + private $refundOrderValidatorMock; /** * @var CreditmemoItemCreationInterface|\PHPUnit_Framework_MockObject_MockObject */ private $creditmemoItemCreationMock; + /** + * @var ValidatorResultInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $validationMessagesMock; + protected function setUp() { $this->resourceConnectionMock = $this->getMockBuilder(ResourceConnection::class) @@ -139,10 +133,7 @@ class RefundOrderTest extends \PHPUnit_Framework_TestCase $this->creditmemoDocumentFactoryMock = $this->getMockBuilder(CreditmemoDocumentFactory::class) ->disableOriginalConstructor() ->getMock(); - $this->creditmemoValidatorMock = $this->getMockBuilder(CreditmemoValidatorInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->orderValidatorMock = $this->getMockBuilder(OrderValidatorInterface::class) + $this->refundOrderValidatorMock = $this->getMockBuilder(RefundOrderInterface::class) ->disableOriginalConstructor() ->getMock(); $this->paymentAdapterMock = $this->getMockBuilder(PaymentAdapterInterface::class) @@ -178,23 +169,22 @@ class RefundOrderTest extends \PHPUnit_Framework_TestCase $this->adapterInterface = $this->getMockBuilder(AdapterInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->itemCreationValidatorMock = $this->getMockBuilder(Order\Creditmemo\ItemCreationValidatorInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); $this->creditmemoItemCreationMock = $this->getMockBuilder(CreditmemoItemCreationInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); + $this->validationMessagesMock = $this->getMockBuilder(ValidatorResultInterface::class) + ->disableOriginalConstructor() + ->setMethods(['hasMessages', 'getMessages', 'addMessage']) + ->getMock(); $this->refundOrder = new RefundOrder( $this->resourceConnectionMock, $this->orderStateResolverMock, $this->orderRepositoryMock, - $this->orderValidatorMock, - $this->creditmemoValidatorMock, - $this->itemCreationValidatorMock, $this->creditmemoRepositoryMock, $this->paymentAdapterMock, $this->creditmemoDocumentFactoryMock, + $this->refundOrderValidatorMock, $this->notifierMock, $this->configMock, $this->loggerMock @@ -202,6 +192,11 @@ class RefundOrderTest extends \PHPUnit_Framework_TestCase } /** + * @param int $orderId + * @param bool $notify + * @param bool $appendComment + * @throws \Magento\Sales\Exception\CouldNotRefundException + * @throws \Magento\Sales\Exception\DocumentValidationException * @dataProvider dataProvider */ public function testOrderCreditmemo($orderId, $notify, $appendComment) @@ -223,22 +218,21 @@ class RefundOrderTest extends \PHPUnit_Framework_TestCase ($appendComment && $notify), $this->creditmemoCreationArgumentsMock )->willReturn($this->creditmemoMock); - $this->creditmemoValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->creditmemoMock) - ->willReturn([]); - $this->orderValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->orderMock) - ->willReturn([]); - $this->itemCreationValidatorMock->expects($this->once()) + $this->refundOrderValidatorMock->expects($this->once()) ->method('validate') ->with( - reset($items), - [CreationQuantityValidator::class], - $this->orderMock - )->willReturn([]); - $this->orderMock->expects($this->once())->method('setCustomerNoteNotify')->with($notify); + $this->orderMock, + $this->creditmemoMock, + $items, + $notify, + $appendComment, + $this->creditmemoCommentCreationMock, + $this->creditmemoCreationArgumentsMock + ) + ->willReturn($this->validationMessagesMock); + $hasMessages = false; + $this->validationMessagesMock->expects($this->once()) + ->method('hasMessages')->willReturn($hasMessages); $this->paymentAdapterMock->expects($this->once()) ->method('refund') ->with($this->creditmemoMock, $this->orderMock) @@ -279,7 +273,6 @@ class RefundOrderTest extends \PHPUnit_Framework_TestCase ->method('notify') ->with($this->orderMock, $this->creditmemoMock, $this->creditmemoCommentCreationMock); } - $this->creditmemoMock->expects($this->once()) ->method('getEntityId') ->willReturn(2); @@ -322,18 +315,23 @@ class RefundOrderTest extends \PHPUnit_Framework_TestCase $this->creditmemoCreationArgumentsMock )->willReturn($this->creditmemoMock); - $this->creditmemoValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->creditmemoMock) - ->willReturn($errorMessages); - $this->orderValidatorMock->expects($this->once()) + $this->refundOrderValidatorMock->expects($this->once()) ->method('validate') - ->with($this->orderMock) - ->willReturn([]); - $this->itemCreationValidatorMock->expects($this->once()) - ->method('validate') - ->with(reset($items), [CreationQuantityValidator::class], $this->orderMock) - ->willReturn([]); + ->with( + $this->orderMock, + $this->creditmemoMock, + $items, + $notify, + $appendComment, + $this->creditmemoCommentCreationMock, + $this->creditmemoCreationArgumentsMock + ) + ->willReturn($this->validationMessagesMock); + $hasMessages = true; + $this->validationMessagesMock->expects($this->once()) + ->method('hasMessages')->willReturn($hasMessages); + $this->validationMessagesMock->expects($this->once()) + ->method('getMessages')->willReturn($errorMessages); $this->assertEquals( $errorMessages, @@ -373,18 +371,21 @@ class RefundOrderTest extends \PHPUnit_Framework_TestCase ($appendComment && $notify), $this->creditmemoCreationArgumentsMock )->willReturn($this->creditmemoMock); - $this->itemCreationValidatorMock->expects($this->once()) + $this->refundOrderValidatorMock->expects($this->once()) ->method('validate') - ->with(reset($items), [CreationQuantityValidator::class], $this->orderMock) - ->willReturn([]); - $this->creditmemoValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->creditmemoMock) - ->willReturn([]); - $this->orderValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->orderMock) - ->willReturn([]); + ->with( + $this->orderMock, + $this->creditmemoMock, + $items, + $notify, + $appendComment, + $this->creditmemoCommentCreationMock, + $this->creditmemoCreationArgumentsMock + ) + ->willReturn($this->validationMessagesMock); + $hasMessages = false; + $this->validationMessagesMock->expects($this->once()) + ->method('hasMessages')->willReturn($hasMessages); $e = new \Exception(); $this->paymentAdapterMock->expects($this->once()) ->method('refund') diff --git a/app/code/Magento/Sales/Test/Unit/Model/ShipOrderTest.php b/app/code/Magento/Sales/Test/Unit/Model/ShipOrderTest.php index b719babf209f0ceb98c4d019f1e5929b23e7e11f..1daf7a64263b817654bf73ee2155506ea9a5f42d 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/ShipOrderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/ShipOrderTest.php @@ -18,11 +18,11 @@ use Magento\Sales\Api\ShipmentRepositoryInterface; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Config as OrderConfig; use Magento\Sales\Model\Order\OrderStateResolverInterface; -use Magento\Sales\Model\Order\OrderValidatorInterface; use Magento\Sales\Model\Order\ShipmentDocumentFactory; use Magento\Sales\Model\Order\Shipment\NotifierInterface; use Magento\Sales\Model\Order\Shipment\OrderRegistrarInterface; -use Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface; +use Magento\Sales\Model\Order\Validation\ShipOrderInterface; +use Magento\Sales\Model\ValidatorResultInterface; use Magento\Sales\Model\ShipOrder; use Psr\Log\LoggerInterface; @@ -49,14 +49,9 @@ class ShipOrderTest extends \PHPUnit_Framework_TestCase private $shipmentDocumentFactoryMock; /** - * @var ShipmentValidatorInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ShipOrderInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $shipmentValidatorMock; - - /** - * @var OrderValidatorInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $orderValidatorMock; + private $shipOrderValidatorMock; /** * @var OrderRegistrarInterface|\PHPUnit_Framework_MockObject_MockObject @@ -109,20 +104,25 @@ class ShipOrderTest extends \PHPUnit_Framework_TestCase private $shipmentMock; /** - * @var AdapterInterface + * @var AdapterInterface|\PHPUnit_Framework_MockObject_MockObject */ private $adapterMock; /** - * @var ShipmentTrackCreationInterface + * @var ShipmentTrackCreationInterface|\PHPUnit_Framework_MockObject_MockObject */ private $trackMock; /** - * @var ShipmentPackageInterface + * @var ShipmentPackageInterface|\PHPUnit_Framework_MockObject_MockObject */ private $packageMock; + /** + * @var ValidatorResultInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $validationMessagesMock; + /** * @var LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ @@ -133,75 +133,58 @@ class ShipOrderTest extends \PHPUnit_Framework_TestCase $this->resourceConnectionMock = $this->getMockBuilder(ResourceConnection::class) ->disableOriginalConstructor() ->getMock(); - $this->orderRepositoryMock = $this->getMockBuilder(OrderRepositoryInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->shipmentDocumentFactoryMock = $this->getMockBuilder(ShipmentDocumentFactory::class) ->disableOriginalConstructor() ->getMock(); - - $this->shipmentValidatorMock = $this->getMockBuilder(ShipmentValidatorInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - - $this->orderValidatorMock = $this->getMockBuilder(OrderValidatorInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->orderRegistrarMock = $this->getMockBuilder(OrderRegistrarInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->orderStateResolverMock = $this->getMockBuilder(OrderStateResolverInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->configMock = $this->getMockBuilder(OrderConfig::class) ->disableOriginalConstructor() ->getMock(); - $this->shipmentRepositoryMock = $this->getMockBuilder(ShipmentRepositoryInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->notifierInterfaceMock = $this->getMockBuilder(NotifierInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->loggerMock = $this->getMockBuilder(LoggerInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->shipmentCommentCreationMock = $this->getMockBuilder(ShipmentCommentCreationInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->shipmentCreationArgumentsMock = $this->getMockBuilder(ShipmentCreationArgumentsInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->orderMock = $this->getMockBuilder(OrderInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->shipmentMock = $this->getMockBuilder(ShipmentInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->packageMock = $this->getMockBuilder(ShipmentPackageInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->trackMock = $this->getMockBuilder(ShipmentTrackCreationInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->adapterMock = $this->getMockBuilder(AdapterInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - + $this->shipOrderValidatorMock = $this->getMockBuilder(ShipOrderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->validationMessagesMock = $this->getMockBuilder(ValidatorResultInterface::class) + ->disableOriginalConstructor() + ->setMethods(['hasMessages', 'getMessages', 'addMessage']) + ->getMock(); $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->model = $helper->getObject( @@ -209,20 +192,25 @@ class ShipOrderTest extends \PHPUnit_Framework_TestCase [ 'resourceConnection' => $this->resourceConnectionMock, 'orderRepository' => $this->orderRepositoryMock, - 'shipmentRepository' => $this->shipmentRepositoryMock, 'shipmentDocumentFactory' => $this->shipmentDocumentFactoryMock, - 'shipmentValidator' => $this->shipmentValidatorMock, - 'orderValidator' => $this->orderValidatorMock, 'orderStateResolver' => $this->orderStateResolverMock, - 'orderRegistrar' => $this->orderRegistrarMock, - 'notifierInterface' => $this->notifierInterfaceMock, 'config' => $this->configMock, - 'logger' => $this->loggerMock + 'shipmentRepository' => $this->shipmentRepositoryMock, + 'shipOrderValidator' => $this->shipOrderValidatorMock, + 'notifierInterface' => $this->notifierInterfaceMock, + 'logger' => $this->loggerMock, + 'orderRegistrar' => $this->orderRegistrarMock ] ); } /** + * @param int $orderId + * @param array $items + * @param bool $notify + * @param bool $appendComment + * @throws \Magento\Sales\Exception\CouldNotShipException + * @throws \Magento\Sales\Exception\DocumentValidationException * @dataProvider dataProvider */ public function testExecute($orderId, $items, $notify, $appendComment) @@ -231,11 +219,9 @@ class ShipOrderTest extends \PHPUnit_Framework_TestCase ->method('getConnection') ->with('sales') ->willReturn($this->adapterMock); - $this->orderRepositoryMock->expects($this->once()) ->method('get') ->willReturn($this->orderMock); - $this->shipmentDocumentFactoryMock->expects($this->once()) ->method('create') ->with( @@ -247,65 +233,61 @@ class ShipOrderTest extends \PHPUnit_Framework_TestCase [$this->packageMock], $this->shipmentCreationArgumentsMock )->willReturn($this->shipmentMock); - - $this->shipmentValidatorMock->expects($this->once()) + $this->shipOrderValidatorMock->expects($this->once()) ->method('validate') - ->with($this->shipmentMock) - ->willReturn([]); - $this->orderValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->orderMock) - ->willReturn([]); - + ->with( + $this->orderMock, + $this->shipmentMock, + $items, + $notify, + $appendComment, + $this->shipmentCommentCreationMock, + [$this->trackMock], + [$this->packageMock] + ) + ->willReturn($this->validationMessagesMock); + $hasMessages = false; + $this->validationMessagesMock->expects($this->once()) + ->method('hasMessages')->willReturn($hasMessages); $this->orderRegistrarMock->expects($this->once()) ->method('register') ->with($this->orderMock, $this->shipmentMock) ->willReturn($this->orderMock); - $this->orderStateResolverMock->expects($this->once()) ->method('getStateForOrder') ->with($this->orderMock, [OrderStateResolverInterface::IN_PROGRESS]) ->willReturn(Order::STATE_PROCESSING); - $this->orderMock->expects($this->once()) ->method('setState') ->with(Order::STATE_PROCESSING) ->willReturnSelf(); - $this->orderMock->expects($this->once()) ->method('getState') ->willReturn(Order::STATE_PROCESSING); - $this->configMock->expects($this->once()) ->method('getStateDefaultStatus') ->with(Order::STATE_PROCESSING) ->willReturn('Processing'); - $this->orderMock->expects($this->once()) ->method('setStatus') ->with('Processing') ->willReturnSelf(); - $this->shipmentRepositoryMock->expects($this->once()) ->method('save') ->with($this->shipmentMock) ->willReturn($this->shipmentMock); - $this->orderRepositoryMock->expects($this->once()) ->method('save') ->with($this->orderMock) ->willReturn($this->orderMock); - if ($notify) { $this->notifierInterfaceMock->expects($this->once()) ->method('notify') ->with($this->orderMock, $this->shipmentMock, $this->shipmentCommentCreationMock); } - $this->shipmentMock->expects($this->once()) ->method('getEntityId') ->willReturn(2); - $this->assertEquals( 2, $this->model->execute( @@ -348,14 +330,24 @@ class ShipOrderTest extends \PHPUnit_Framework_TestCase $this->shipmentCreationArgumentsMock )->willReturn($this->shipmentMock); - $this->shipmentValidatorMock->expects($this->once()) + $this->shipOrderValidatorMock->expects($this->once()) ->method('validate') - ->with($this->shipmentMock) - ->willReturn($errorMessages); - $this->orderValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->orderMock) - ->willReturn([]); + ->with( + $this->orderMock, + $this->shipmentMock, + $items, + $notify, + $appendComment, + $this->shipmentCommentCreationMock, + [$this->trackMock], + [$this->packageMock] + ) + ->willReturn($this->validationMessagesMock); + $hasMessages = true; + $this->validationMessagesMock->expects($this->once()) + ->method('hasMessages')->willReturn($hasMessages); + $this->validationMessagesMock->expects($this->once()) + ->method('getMessages')->willReturn($errorMessages); $this->model->execute( $orderId, @@ -372,9 +364,12 @@ class ShipOrderTest extends \PHPUnit_Framework_TestCase /** * @expectedException \Magento\Sales\Api\Exception\CouldNotShipExceptionInterface */ - public function testCouldNotInvoiceException() + public function testCouldNotShipException() { $orderId = 1; + $items = [1 => 2]; + $notify = true; + $appendComment = true; $this->resourceConnectionMock->expects($this->once()) ->method('getConnection') ->with('sales') @@ -387,33 +382,53 @@ class ShipOrderTest extends \PHPUnit_Framework_TestCase $this->shipmentDocumentFactoryMock->expects($this->once()) ->method('create') ->with( - $this->orderMock + $this->orderMock, + $items, + [$this->trackMock], + $this->shipmentCommentCreationMock, + ($appendComment && $notify), + [$this->packageMock], + $this->shipmentCreationArgumentsMock )->willReturn($this->shipmentMock); - - $this->shipmentValidatorMock->expects($this->once()) + $this->shipOrderValidatorMock->expects($this->once()) ->method('validate') - ->with($this->shipmentMock) - ->willReturn([]); - $this->orderValidatorMock->expects($this->once()) - ->method('validate') - ->with($this->orderMock) - ->willReturn([]); - $e = new \Exception(); + ->with( + $this->orderMock, + $this->shipmentMock, + $items, + $notify, + $appendComment, + $this->shipmentCommentCreationMock, + [$this->trackMock], + [$this->packageMock] + ) + ->willReturn($this->validationMessagesMock); + $hasMessages = false; + $this->validationMessagesMock->expects($this->once()) + ->method('hasMessages')->willReturn($hasMessages); + $exception = new \Exception(); $this->orderRegistrarMock->expects($this->once()) ->method('register') ->with($this->orderMock, $this->shipmentMock) - ->willThrowException($e); + ->willThrowException($exception); $this->loggerMock->expects($this->once()) ->method('critical') - ->with($e); + ->with($exception); $this->adapterMock->expects($this->once()) ->method('rollBack'); $this->model->execute( - $orderId + $orderId, + $items, + $notify, + $appendComment, + $this->shipmentCommentCreationMock, + [$this->trackMock], + [$this->packageMock], + $this->shipmentCreationArgumentsMock ); } diff --git a/app/code/Magento/Sales/etc/di.xml b/app/code/Magento/Sales/etc/di.xml index 4b921587c292b54bdd49a40cb174a7de373917f5..4e2487d9dcada2eaf224f8cc30f65fc8fa4a4fc8 100644 --- a/app/code/Magento/Sales/etc/di.xml +++ b/app/code/Magento/Sales/etc/di.xml @@ -105,6 +105,11 @@ <preference for="Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface" type="Magento\Sales\Model\Order\Shipment\ShipmentValidator"/> <preference for="Magento\Sales\Model\Order\Creditmemo\CreditmemoValidatorInterface" type="Magento\Sales\Model\Order\Creditmemo\CreditmemoValidator"/> <preference for="Magento\Sales\Model\Order\Creditmemo\ItemCreationValidatorInterface" type="Magento\Sales\Model\Order\Creditmemo\ItemCreationValidator"/> + <preference for="Magento\Sales\Model\ValidatorResultInterface" type="Magento\Sales\Model\ValidatorResult"/> + <preference for="Magento\Sales\Model\Order\Validation\InvoiceOrderInterface" type="Magento\Sales\Model\Order\Validation\InvoiceOrder"/> + <preference for="Magento\Sales\Model\Order\Validation\RefundInvoiceInterface" type="Magento\Sales\Model\Order\Validation\RefundInvoice"/> + <preference for="Magento\Sales\Model\Order\Validation\RefundOrderInterface" type="Magento\Sales\Model\Order\Validation\RefundOrder"/> + <preference for="Magento\Sales\Model\Order\Validation\ShipOrderInterface" type="Magento\Sales\Model\Order\Validation\ShipOrder"/> <preference for="Magento\Sales\Model\Order\Creditmemo\NotifierInterface" type="Magento\Sales\Model\Order\Creditmemo\Notifier"/> <preference for="Magento\Sales\Api\RefundOrderInterface" type="Magento\Sales\Model\RefundOrder"/> <preference for="Magento\Sales\Api\RefundInvoiceInterface" type="Magento\Sales\Model\RefundInvoice"/> diff --git a/app/code/Magento/SalesInventory/LICENSE.txt b/app/code/Magento/SalesInventory/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..49525fd99da9c51e6d85420266d41cb3d6b7a648 --- /dev/null +++ b/app/code/Magento/SalesInventory/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/SalesInventory/LICENSE_AFL.txt b/app/code/Magento/SalesInventory/LICENSE_AFL.txt new file mode 100644 index 0000000000000000000000000000000000000000..f39d641b18a19e56df6c8a3e4038c940fb886b32 --- /dev/null +++ b/app/code/Magento/SalesInventory/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +Academic Free License ("AFL") v. 3.0 + +This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Academic Free License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/SalesInventory/Model/Order/ReturnProcessor.php b/app/code/Magento/SalesInventory/Model/Order/ReturnProcessor.php new file mode 100644 index 0000000000000000000000000000000000000000..7752d7131031cf0daa63f4c8b8bf49609e3090ed --- /dev/null +++ b/app/code/Magento/SalesInventory/Model/Order/ReturnProcessor.php @@ -0,0 +1,156 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesInventory\Model\Order; + +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\Data\CreditmemoItemInterface; +use Magento\Sales\Api\Data\OrderInterface; + +/** + * Class ReturnProcessor + */ +class ReturnProcessor +{ + /** + * @var \Magento\CatalogInventory\Api\StockManagementInterface + */ + private $stockManagement; + + /** + * @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor + */ + private $stockIndexerProcessor; + + /** + * @var \Magento\Catalog\Model\Indexer\Product\Price\Processor + */ + private $priceIndexer; + + /** + * @var \Magento\Sales\Api\CreditmemoRepositoryInterface + */ + private $creditmemoRepository; + + /** + * @var \Magento\Store\Model\StoreManagerInterface + */ + private $storeManager; + + /** + * @var \Magento\Sales\Api\OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var \Magento\Sales\Api\OrderItemRepositoryInterface + */ + private $orderItemRepository; + + /** + * ReturnToStockPlugin constructor. + * @param \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration + * @param \Magento\CatalogInventory\Api\StockManagementInterface $stockManagement + * @param \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexer + * @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $priceIndexer + * @param \Magento\Sales\Api\CreditmemoRepositoryInterface $creditmemoRepository + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository + * @param \Magento\Sales\Api\OrderItemRepositoryInterface $orderItemRepository + */ + public function __construct( + \Magento\CatalogInventory\Api\StockManagementInterface $stockManagement, + \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexer, + \Magento\Catalog\Model\Indexer\Product\Price\Processor $priceIndexer, + \Magento\Sales\Api\CreditmemoRepositoryInterface $creditmemoRepository, + \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, + \Magento\Sales\Api\OrderItemRepositoryInterface $orderItemRepository + ) { + $this->stockManagement = $stockManagement; + $this->stockIndexerProcessor = $stockIndexer; + $this->priceIndexer = $priceIndexer; + $this->creditmemoRepository = $creditmemoRepository; + $this->storeManager = $storeManager; + $this->orderRepository = $orderRepository; + $this->orderItemRepository = $orderItemRepository; + } + + /** + * @param CreditmemoInterface $creditmemo + * @param OrderInterface $order + * @param array $returnToStockItems + * @return void + */ + public function execute( + CreditmemoInterface $creditmemo, + OrderInterface $order, + array $returnToStockItems = [] + ) { + $itemsToUpdate = []; + foreach ($creditmemo->getItems() as $item) { + $qty = $item->getQty(); + $productId = $item->getProductId(); + $orderItem = $this->orderItemRepository->get($item->getOrderItemId()); + $parentItemId = $orderItem->getParentItemId(); + if ($this->canReturnItem($item, $qty, $parentItemId, $returnToStockItems)) { + $parentItem = $parentItemId ? $this->getItemByOrderId($creditmemo, $parentItemId) : false; + $qty = $parentItem ? $parentItem->getQty() * $qty : $qty; + if (isset($itemsToUpdate[$productId])) { + $itemsToUpdate[$productId] += $qty; + } else { + $itemsToUpdate[$productId] = $qty; + } + } + } + + if (!empty($itemsToUpdate)) { + $store = $this->storeManager->getStore($order->getStoreId()); + foreach ($itemsToUpdate as $productId => $qty) { + $this->stockManagement->backItemQty( + $productId, + $qty, + $store->getWebsiteId() + ); + } + + $updatedItemIds = array_keys($itemsToUpdate); + $this->stockIndexerProcessor->reindexList($updatedItemIds); + $this->priceIndexer->reindexList($updatedItemIds); + } + } + + /** + * @param \Magento\Sales\Api\Data\CreditmemoInterface $creditmemo + * @param int $parentItemId + * @return bool|CreditmemoItemInterface + */ + private function getItemByOrderId(\Magento\Sales\Api\Data\CreditmemoInterface $creditmemo, $parentItemId) + { + foreach ($creditmemo->getItems() as $item) { + if ($item->getOrderItemId() == $parentItemId) { + return $item; + } + } + return false; + } + + /** + * @param \Magento\Sales\Api\Data\CreditmemoItemInterface $item + * @param int $qty + * @param int[] $returnToStockItems + * @param int $parentItemId + * @return bool + */ + private function canReturnItem( + \Magento\Sales\Api\Data\CreditmemoItemInterface $item, + $qty, + $parentItemId = null, + array $returnToStockItems = [] + ) { + return (in_array($item->getOrderItemId(), $returnToStockItems) || in_array($parentItemId, $returnToStockItems)) + && $qty; + } +} diff --git a/app/code/Magento/SalesInventory/Model/Order/ReturnValidator.php b/app/code/Magento/SalesInventory/Model/Order/ReturnValidator.php new file mode 100644 index 0000000000000000000000000000000000000000..2195883334fcf6dfa21a55a49797f64a4ec0faa4 --- /dev/null +++ b/app/code/Magento/SalesInventory/Model/Order/ReturnValidator.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesInventory\Model\Order; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\Data\CreditmemoItemInterface; +use Magento\Sales\Api\Data\OrderItemInterface; +use Magento\Sales\Api\OrderItemRepositoryInterface; + +/** + * Class ReturnProcessor + */ +class ReturnValidator +{ + /** + * @var OrderItemRepositoryInterface + */ + private $orderItemRepository; + + /** + * ReturnValidator constructor. + * @param OrderItemRepositoryInterface $orderItemRepository + */ + public function __construct(OrderItemRepositoryInterface $orderItemRepository) + { + $this->orderItemRepository = $orderItemRepository; + } + + /** + * @param int[] $returnToStockItems + * @param CreditmemoInterface $creditmemo + * @return \Magento\Framework\Phrase|null + */ + public function validate($returnToStockItems, CreditmemoInterface $creditmemo) + { + $creditmemoItems = $creditmemo->getItems(); + + /** @var int $item */ + foreach ($returnToStockItems as $item) { + try { + $orderItem = $this->orderItemRepository->get($item); + if (!$this->isOrderItemPartOfCreditmemo($creditmemoItems, $orderItem)) { + return __('The "%1" product is not part of the current creditmemo.', $orderItem->getSku()); + } + } catch (NoSuchEntityException $e) { + return __('The return to stock argument contains product item that is not part of the original order.'); + } + } + return null; + } + + /** + * @param CreditmemoItemInterface[] $creditmemoItems + * @param OrderItemInterface $orderItem + * @return bool + */ + private function isOrderItemPartOfCreditmemo(array $creditmemoItems, OrderItemInterface $orderItem) + { + foreach ($creditmemoItems as $creditmemoItem) { + if ($creditmemoItem->getOrderItemId() == $orderItem->getItemId()) { + return true; + } + } + return false; + } +} diff --git a/app/code/Magento/SalesInventory/Model/Plugin/Order/ReturnToStockInvoice.php b/app/code/Magento/SalesInventory/Model/Plugin/Order/ReturnToStockInvoice.php new file mode 100644 index 0000000000000000000000000000000000000000..53a393d83353b4922f61641160dcf3c2aa8b6d85 --- /dev/null +++ b/app/code/Magento/SalesInventory/Model/Plugin/Order/ReturnToStockInvoice.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesInventory\Model\Plugin\Order; + +/** + * Class ReturnToStockInvoice + */ +class ReturnToStockInvoice +{ + /** + * @var \Magento\SalesInventory\Model\Order\ReturnProcessor + */ + private $returnProcessor; + + /** + * @var \Magento\Sales\Api\CreditmemoRepositoryInterface + */ + private $creditmemoRepository; + + /** + * @var \Magento\Sales\Api\OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var \Magento\Sales\Api\InvoiceRepositoryInterface + */ + private $invoiceRepository; + + /** + * @var \Magento\CatalogInventory\Api\StockConfigurationInterface + */ + private $stockConfiguration; + + /** + * ReturnToStockInvoice constructor. + * @param \Magento\SalesInventory\Model\Order\ReturnProcessor $returnProcessor + * @param \Magento\Sales\Api\CreditmemoRepositoryInterface $creditmemoRepository + * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository + * @param \Magento\Sales\Api\InvoiceRepositoryInterface $invoiceRepository + * @param \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration + */ + public function __construct( + \Magento\SalesInventory\Model\Order\ReturnProcessor $returnProcessor, + \Magento\Sales\Api\CreditmemoRepositoryInterface $creditmemoRepository, + \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, + \Magento\Sales\Api\InvoiceRepositoryInterface $invoiceRepository, + \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration + ) { + $this->returnProcessor = $returnProcessor; + $this->creditmemoRepository = $creditmemoRepository; + $this->orderRepository = $orderRepository; + $this->invoiceRepository = $invoiceRepository; + $this->stockConfiguration = $stockConfiguration; + } + + /** + * @param \Magento\Sales\Api\RefundInvoiceInterface $refundService + * @param int $resultEntityId + * @param int $invoiceId + * @param \Magento\Sales\Api\Data\CreditmemoItemCreationInterface[] $items + * @param bool|null $isOnline + * @param bool|null $notify + * @param bool|null $appendComment + * @param \Magento\Sales\Api\Data\CreditmemoCommentCreationInterface|null $comment + * @param \Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface|null $arguments + * @return int + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterExecute( + \Magento\Sales\Api\RefundInvoiceInterface $refundService, + $resultEntityId, + $invoiceId, + array $items = [], + $isOnline = false, + $notify = false, + $appendComment = false, + \Magento\Sales\Api\Data\CreditmemoCommentCreationInterface $comment = null, + \Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface $arguments = null + ) { + if ($this->stockConfiguration->isAutoReturnEnabled()) { + return $resultEntityId; + } + + $invoice = $this->invoiceRepository->get($invoiceId); + $order = $this->orderRepository->get($invoice->getOrderId()); + + $returnToStockItems = []; + if ($arguments !== null + && $arguments->getExtensionAttributes() !== null + && $arguments->getExtensionAttributes()->getReturnToStockItems() !== null + ) { + $returnToStockItems = $arguments->getExtensionAttributes()->getReturnToStockItems(); + } + + $creditmemo = $this->creditmemoRepository->get($resultEntityId); + $this->returnProcessor->execute($creditmemo, $order, $returnToStockItems); + + return $resultEntityId; + } +} diff --git a/app/code/Magento/SalesInventory/Model/Plugin/Order/ReturnToStockOrder.php b/app/code/Magento/SalesInventory/Model/Plugin/Order/ReturnToStockOrder.php new file mode 100644 index 0000000000000000000000000000000000000000..b21f9f6b1050636738a67c88151ceb1780f3b820 --- /dev/null +++ b/app/code/Magento/SalesInventory/Model/Plugin/Order/ReturnToStockOrder.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesInventory\Model\Plugin\Order; + +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\SalesInventory\Model\Order\ReturnProcessor; +use Magento\Sales\Api\CreditmemoRepositoryInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Api\RefundOrderInterface; + +/** + * Class ReturnToStock + */ +class ReturnToStockOrder +{ + /** + * @var ReturnProcessor + */ + private $returnProcessor; + + /** + * @var CreditmemoRepositoryInterface + */ + private $creditmemoRepository; + + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @var StockConfigurationInterface + */ + private $stockConfiguration; + + /** + * ReturnToStockPlugin constructor. + * + * @param ReturnProcessor $returnProcessor + * @param CreditmemoRepositoryInterface $creditmemoRepository + * @param OrderRepositoryInterface $orderRepository + * @param StockConfigurationInterface $stockConfiguration + */ + public function __construct( + ReturnProcessor $returnProcessor, + CreditmemoRepositoryInterface $creditmemoRepository, + OrderRepositoryInterface $orderRepository, + StockConfigurationInterface $stockConfiguration + ) { + $this->returnProcessor = $returnProcessor; + $this->creditmemoRepository = $creditmemoRepository; + $this->orderRepository = $orderRepository; + $this->stockConfiguration = $stockConfiguration; + } + + /** + * @param RefundOrderInterface $refundService + * @param int $resultEntityId + * @param int $orderId + * @param \Magento\Sales\Api\Data\CreditmemoItemCreationInterface[] $items + * @param bool|null $notify + * @param bool|null $appendComment + * @param \Magento\Sales\Api\Data\CreditmemoCommentCreationInterface|null $comment + * @param \Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface|null $arguments + * @return int + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterExecute( + RefundOrderInterface $refundService, + $resultEntityId, + $orderId, + array $items = [], + $notify = false, + $appendComment = false, + \Magento\Sales\Api\Data\CreditmemoCommentCreationInterface $comment = null, + \Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface $arguments = null + ) { + if ($this->stockConfiguration->isAutoReturnEnabled()) { + return $resultEntityId; + } + + $order = $this->orderRepository->get($orderId); + + $returnToStockItems = []; + if ($arguments !== null + && $arguments->getExtensionAttributes() !== null + && $arguments->getExtensionAttributes()->getReturnToStockItems() !== null + ) { + $returnToStockItems = $arguments->getExtensionAttributes()->getReturnToStockItems(); + } + + $creditmemo = $this->creditmemoRepository->get($resultEntityId); + $this->returnProcessor->execute($creditmemo, $order, $returnToStockItems); + + return $resultEntityId; + } +} diff --git a/app/code/Magento/SalesInventory/Model/Plugin/Order/Validation/InvoiceRefundCreationArguments.php b/app/code/Magento/SalesInventory/Model/Plugin/Order/Validation/InvoiceRefundCreationArguments.php new file mode 100644 index 0000000000000000000000000000000000000000..7dfa2893c588f713438ee630e8f7fcc7a670bb32 --- /dev/null +++ b/app/code/Magento/SalesInventory/Model/Plugin/Order/Validation/InvoiceRefundCreationArguments.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesInventory\Model\Plugin\Order\Validation; + +use Magento\Sales\Api\Data\InvoiceInterface; +use Magento\Sales\Model\Order\Validation\RefundInvoiceInterface; +use Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface; +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\SalesInventory\Model\Order\ReturnValidator; +use Magento\Sales\Model\ValidatorResultInterface; + +/** + * Class CreditmemoCreationArguments + */ +class InvoiceRefundCreationArguments +{ + /** + * @var ReturnValidator + */ + private $returnValidator; + + /** + * InvoiceRefundCreationArguments constructor. + * @param ReturnValidator $returnValidator + */ + public function __construct( + ReturnValidator $returnValidator + ) { + $this->returnValidator = $returnValidator; + } + + /** + * @param RefundInvoiceInterface $refundInvoiceValidator + * @param ValidatorResultInterface $validationResults + * @param InvoiceInterface $invoice + * @param OrderInterface $order + * @param CreditmemoInterface $creditmemo + * @param array $items + * @param bool $isOnline + * @param bool $notify + * @param bool $appendComment + * @param \Magento\Sales\Api\Data\CreditmemoCommentCreationInterface|null $comment + * @param \Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface|null $arguments + * @return ValidatorResultInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function afterValidate( + RefundInvoiceInterface $refundInvoiceValidator, + ValidatorResultInterface $validationResults, + InvoiceInterface $invoice, + OrderInterface $order, + CreditmemoInterface $creditmemo, + array $items = [], + $isOnline = false, + $notify = false, + $appendComment = false, + \Magento\Sales\Api\Data\CreditmemoCommentCreationInterface $comment = null, + \Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface $arguments = null + ) { + if ($this->isReturnToStockItems($arguments)) { + return $validationResults; + } + + /** @var int[] $returnToStockItems */ + $returnToStockItems = $arguments->getExtensionAttributes()->getReturnToStockItems(); + $validationMessage = $this->returnValidator->validate($returnToStockItems, $creditmemo); + if ($validationMessage) { + $validationResults->addMessage($validationMessage); + } + + return $validationResults; + } + + /** + * @param CreditmemoCreationArgumentsInterface|null $arguments + * @return bool + */ + private function isReturnToStockItems($arguments) + { + return $arguments === null + || $arguments->getExtensionAttributes() === null + || $arguments->getExtensionAttributes()->getReturnToStockItems() === null; + } +} diff --git a/app/code/Magento/SalesInventory/Model/Plugin/Order/Validation/OrderRefundCreationArguments.php b/app/code/Magento/SalesInventory/Model/Plugin/Order/Validation/OrderRefundCreationArguments.php new file mode 100644 index 0000000000000000000000000000000000000000..29411c7c5e8c6b072203267660f8eafef1306ab7 --- /dev/null +++ b/app/code/Magento/SalesInventory/Model/Plugin/Order/Validation/OrderRefundCreationArguments.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesInventory\Model\Plugin\Order\Validation; + +use Magento\Sales\Api\Data\CreditmemoCommentCreationInterface; +use Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface; +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order\Validation\RefundOrderInterface; +use Magento\SalesInventory\Model\Order\ReturnValidator; +use Magento\Sales\Model\ValidatorResultInterface; + +/** + * Class OrderRefundCreationArguments + */ +class OrderRefundCreationArguments +{ + /** + * @var ReturnValidator + */ + private $returnValidator; + + /** + * OrderRefundCreationArguments constructor. + * @param ReturnValidator $returnValidator + */ + public function __construct( + ReturnValidator $returnValidator + ) { + $this->returnValidator = $returnValidator; + } + + /** + * @param RefundOrderInterface $refundOrderValidator + * @param ValidatorResultInterface $validationResults + * @param OrderInterface $order + * @param CreditmemoInterface $creditmemo + * @param array $items + * @param bool $notify + * @param bool $appendComment + * @param CreditmemoCommentCreationInterface|null $comment + * @param CreditmemoCreationArgumentsInterface|null $arguments + * @return ValidatorResultInterface + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterValidate( + RefundOrderInterface $refundOrderValidator, + ValidatorResultInterface $validationResults, + OrderInterface $order, + CreditmemoInterface $creditmemo, + array $items = [], + $notify = false, + $appendComment = false, + CreditmemoCommentCreationInterface $comment = null, + CreditmemoCreationArgumentsInterface $arguments = null + ) { + if ($this->isReturnToStockItems($arguments)) { + return $validationResults; + } + + $returnToStockItems = $arguments->getExtensionAttributes()->getReturnToStockItems(); + $validationMessage = $this->returnValidator->validate($returnToStockItems, $creditmemo); + if ($validationMessage) { + $validationResults->addMessage($validationMessage); + } + + return $validationResults; + } + + /** + * @param CreditmemoCreationArgumentsInterface|null $arguments + * @return bool + */ + private function isReturnToStockItems($arguments) + { + return $arguments === null + || $arguments->getExtensionAttributes() === null + || $arguments->getExtensionAttributes()->getReturnToStockItems() === null; + } +} diff --git a/app/code/Magento/SalesInventory/README.md b/app/code/Magento/SalesInventory/README.md new file mode 100644 index 0000000000000000000000000000000000000000..915569ddd3ce24a240f5938c4e38e5275e9ada2c --- /dev/null +++ b/app/code/Magento/SalesInventory/README.md @@ -0,0 +1 @@ +Magento_SalesInventory module allows retrieve and update stock attributes related to Magento_Sales, such as status and quantity. diff --git a/app/code/Magento/SalesInventory/Test/Unit/Model/Order/ReturnProcessorTest.php b/app/code/Magento/SalesInventory/Test/Unit/Model/Order/ReturnProcessorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..523759d54645a1e2db471db948fa116cf59cd32e --- /dev/null +++ b/app/code/Magento/SalesInventory/Test/Unit/Model/Order/ReturnProcessorTest.php @@ -0,0 +1,195 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesInventory\Test\Unit\Model\Order; + +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\CatalogInventory\Api\StockManagementInterface; +use Magento\Sales\Api\CreditmemoRepositoryInterface; +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\Data\CreditmemoItemInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\Data\OrderItemInterface; +use Magento\Sales\Api\OrderItemRepositoryInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\SalesInventory\Model\Order\ReturnProcessor; + +/** + * Class ReturnProcessorTest + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ReturnProcessorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject|OrderInterface + */ + private $orderMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|CreditmemoInterface + */ + private $creditmemoMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|StockManagementInterface + */ + private $stockManagementMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\CatalogInventory\Model\Indexer\Stock\Processor + */ + private $stockIndexerProcessorMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Catalog\Model\Indexer\Product\Price\Processor + */ + private $priceIndexerMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|CreditmemoRepositoryInterface + */ + private $creditmemoRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|StoreManagerInterface + */ + private $storeManagerMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|OrderRepositoryInterface + */ + private $orderRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|OrderItemRepositoryInterface + */ + private $orderItemRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|CreditmemoItemInterface + */ + private $creditmemoItemMock; + + /** @var ReturnProcessor */ + private $returnProcessor; + + /** @var \PHPUnit_Framework_MockObject_MockObject|OrderItemInterface */ + private $orderItemMock; + + /** @var \PHPUnit_Framework_MockObject_MockObject|StoreInterface */ + private $storeMock; + + public function setUp() + { + $this->stockManagementMock = $this->getMockBuilder(StockManagementInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->stockIndexerProcessorMock = $this->getMockBuilder( + \Magento\CatalogInventory\Model\Indexer\Stock\Processor::class + )->disableOriginalConstructor() + ->getMock(); + $this->priceIndexerMock = $this->getMockBuilder(\Magento\Catalog\Model\Indexer\Product\Price\Processor::class) + ->disableOriginalConstructor() + ->getMock(); + $this->creditmemoRepositoryMock = $this->getMockBuilder(CreditmemoRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->orderRepositoryMock = $this->getMockBuilder(OrderRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->orderItemRepositoryMock = $this->getMockBuilder(OrderItemRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->orderMock = $this->getMockBuilder(OrderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->creditmemoMock = $this->getMockBuilder(CreditmemoInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->creditmemoItemMock = $this->getMockBuilder(CreditmemoItemInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->orderItemMock = $this->getMockBuilder(OrderItemInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->storeMock = $this->getMockBuilder(StoreInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->returnProcessor = new ReturnProcessor( + $this->stockManagementMock, + $this->stockIndexerProcessorMock, + $this->priceIndexerMock, + $this->creditmemoRepositoryMock, + $this->storeManagerMock, + $this->orderRepositoryMock, + $this->orderItemRepositoryMock + ); + } + + public function testExecute() + { + $orderItemId = 99; + $productId = 50; + $returnToStockItems = [$orderItemId]; + $qty = 1; + $storeId = 0; + $webSiteId = 10; + + $this->creditmemoMock->expects($this->once()) + ->method('getItems') + ->willReturn([$this->creditmemoItemMock]); + + $this->creditmemoItemMock->expects($this->once()) + ->method('getQty') + ->willReturn($qty); + + $this->creditmemoItemMock->expects($this->exactly(2)) + ->method('getOrderItemId') + ->willReturn($orderItemId); + + $this->creditmemoItemMock->expects($this->once()) + ->method('getProductId') + ->willReturn($productId); + + $this->orderItemRepositoryMock->expects($this->once()) + ->method('get') + ->with($orderItemId) + ->willReturn($this->orderItemMock); + + $this->orderMock->expects($this->once()) + ->method('getStoreId') + ->willReturn($storeId); + + $this->storeManagerMock->expects($this->once()) + ->method('getStore') + ->with($storeId) + ->willReturn($this->storeMock); + + $this->storeMock->expects($this->once()) + ->method('getWebsiteId') + ->willReturn($webSiteId); + + $this->stockManagementMock->expects($this->once()) + ->method('backItemQty') + ->with($productId, $qty, $webSiteId) + ->willReturn(true); + + $this->stockIndexerProcessorMock->expects($this->once()) + ->method('reindexList') + ->with([$productId]); + + $this->priceIndexerMock->expects($this->once()) + ->method('reindexList') + ->with([$productId]); + + $this->returnProcessor->execute($this->creditmemoMock, $this->orderMock, $returnToStockItems); + } +} diff --git a/app/code/Magento/SalesInventory/Test/Unit/Model/Order/ReturnValidatorTest.php b/app/code/Magento/SalesInventory/Test/Unit/Model/Order/ReturnValidatorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..da9d3fd8aefd103965f2d577b303e7e1a522223b --- /dev/null +++ b/app/code/Magento/SalesInventory/Test/Unit/Model/Order/ReturnValidatorTest.php @@ -0,0 +1,134 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesInventory\Test\Unit\Model\Order; + +use Magento\SalesInventory\Model\Order\ReturnValidator; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\Data\CreditmemoItemInterface; +use Magento\Sales\Api\Data\OrderItemInterface; +use Magento\Sales\Api\OrderItemRepositoryInterface; + +/** + * Class ReturnValidatorTest + */ +class ReturnValidatorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject|OrderItemRepositoryInterface + */ + private $orderItemRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|CreditmemoInterface + */ + private $creditMemoMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|CreditmemoItemInterface + */ + private $creditMemoItemMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|OrderItemInterface + */ + private $orderItemMock; + + /** + * @var ReturnValidator + */ + private $returnValidator; + + protected function setUp() + { + $this->orderItemRepositoryMock = $this->getMockBuilder(OrderItemRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->creditMemoMock = $this->getMockBuilder(CreditmemoInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->creditMemoItemMock = $this->getMockBuilder(CreditmemoItemInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->orderItemMock = $this->getMockBuilder(OrderItemInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->returnValidator = new ReturnValidator( + $this->orderItemRepositoryMock + ); + } + + /** + * @dataProvider dataProvider + */ + public function testValidate( + $expectedResult, + $returnToStockItems, + $orderItemId, + $creditMemoItemId, + $productSku = null + ) { + $this->creditMemoMock->expects($this->once()) + ->method('getItems') + ->willReturn([$this->creditMemoItemMock]); + + $this->orderItemRepositoryMock->expects($this->once()) + ->method('get') + ->with($returnToStockItems[0]) + ->willReturn($this->orderItemMock); + + $this->orderItemMock->expects($this->once()) + ->method('getItemId') + ->willReturn($orderItemId); + + $this->creditMemoItemMock->expects($this->once()) + ->method('getOrderItemId') + ->willReturn($creditMemoItemId); + + if ($productSku) { + $this->orderItemMock->expects($this->once()) + ->method('getSku') + ->willReturn($productSku); + } + + $this->assertEquals( + $this->returnValidator->validate($returnToStockItems, $this->creditMemoMock), + $expectedResult + ); + } + + public function testValidationWithWrongOrderItems() + { + $returnToStockItems = [1]; + $this->creditMemoMock->expects($this->once()) + ->method('getItems') + ->willReturn([$this->creditMemoItemMock]); + $this->orderItemRepositoryMock->expects($this->once()) + ->method('get') + ->with($returnToStockItems[0]) + ->willThrowException(new NoSuchEntityException); + + $this->assertEquals( + $this->returnValidator->validate($returnToStockItems, $this->creditMemoMock), + __('The return to stock argument contains product item that is not part of the original order.') + ); + + } + + public function dataProvider() + { + return [ + 'PostirivValidationTest' => [null, [1], 1, 1], + 'WithWrongReturnToStockItems' => [ + __('The "%1" product is not part of the current creditmemo.', 'sku1'), [2], 2, 1, 'sku1', + ], + ]; + } +} diff --git a/app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/ReturnToStockInvoiceTest.php b/app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/ReturnToStockInvoiceTest.php new file mode 100644 index 0000000000000000000000000000000000000000..35718a96bd509cc8b021c286ca44c042a8a1d67d --- /dev/null +++ b/app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/ReturnToStockInvoiceTest.php @@ -0,0 +1,179 @@ +<?php +/** + * + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesInventory\Test\Unit\Model\Plugin\Order; + +/** + * Class ReturnToStockInvoiceTest + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ReturnToStockInvoiceTest extends \PHPUnit_Framework_TestCase +{ + /** @var \Magento\SalesInventory\Model\Plugin\Order\ReturnToStockInvoice */ + private $returnTOStock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\SalesInventory\Model\Order\ReturnProcessor + */ + private $returnProcessorMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Sales\Api\CreditmemoRepositoryInterface + */ + private $creditmemoRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Sales\Api\InvoiceRepositoryInterface + */ + private $invoiceRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Sales\Api\OrderRepositoryInterface + */ + private $orderRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Sales\Api\RefundOrderInterface + */ + private $refundInvoiceMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface + */ + private $creditmemoCreationArgumentsMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Sales\Api\Data\OrderInterface + */ + private $orderMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Sales\Api\Data\CreditmemoInterface + */ + private $creditmemoMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Sales\Api\Data\InvoiceInterface + */ + private $invoiceMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface + */ + private $extencionAttributesMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\CatalogInventory\Api\StockConfigurationInterface + */ + private $stockConfigurationMock; + + protected function setUp() + { + $this->returnProcessorMock = $this->getMockBuilder(\Magento\SalesInventory\Model\Order\ReturnProcessor::class) + ->disableOriginalConstructor() + ->getMock(); + $this->creditmemoRepositoryMock = $this->getMockBuilder(\Magento\Sales\Api\CreditmemoRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->orderRepositoryMock = $this->getMockBuilder(\Magento\Sales\Api\OrderRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->invoiceRepositoryMock = $this->getMockBuilder(\Magento\Sales\Api\InvoiceRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->refundInvoiceMock = $this->getMockBuilder(\Magento\Sales\Api\RefundInvoiceInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->creditmemoCreationArgumentsMock = $this->getMockBuilder( + \Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface::class + )->disableOriginalConstructor() + ->getMock(); + $this->extencionAttributesMock = $this->getMockBuilder( + \Magento\Sales\Api\Data\CreditmemoCreationArgumentsExtensionInterface::class + )->disableOriginalConstructor() + ->setMethods(['getReturnToStockItems']) + ->getMock(); + $this->orderMock = $this->getMockBuilder(\Magento\Sales\Api\Data\OrderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->creditmemoMock = $this->getMockBuilder(\Magento\Sales\Api\Data\CreditmemoInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->invoiceMock = $this->getMockBuilder(\Magento\Sales\Api\Data\InvoiceInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->stockConfigurationMock = $this->getMockBuilder( + \Magento\CatalogInventory\Api\StockConfigurationInterface::class + )->disableOriginalConstructor() + ->getMock(); + + $this->returnTOStock = new \Magento\SalesInventory\Model\Plugin\Order\ReturnToStockInvoice( + $this->returnProcessorMock, + $this->creditmemoRepositoryMock, + $this->orderRepositoryMock, + $this->invoiceRepositoryMock, + $this->stockConfigurationMock + ); + } + + public function testAfterExecute() + { + $orderId = 1; + $creditmemoId = 99; + $items = []; + $returnToStockItems = [1]; + $invoiceId = 98; + $this->creditmemoCreationArgumentsMock->expects($this->exactly(3)) + ->method('getExtensionAttributes') + ->willReturn($this->extencionAttributesMock); + + $this->invoiceRepositoryMock->expects($this->once()) + ->method('get') + ->with($invoiceId) + ->willReturn($this->invoiceMock); + + $this->extencionAttributesMock->expects($this->exactly(2)) + ->method('getReturnToStockItems') + ->willReturn($returnToStockItems); + + $this->orderRepositoryMock->expects($this->once()) + ->method('get') + ->with($orderId) + ->willReturn($this->orderMock); + + $this->creditmemoRepositoryMock->expects($this->once()) + ->method('get') + ->with($creditmemoId) + ->willReturn($this->creditmemoMock); + + $this->returnProcessorMock->expects($this->once()) + ->method('execute') + ->with($this->creditmemoMock, $this->orderMock, $returnToStockItems); + + $this->invoiceMock->expects($this->once()) + ->method('getOrderId') + ->willReturn($orderId); + + $this->stockConfigurationMock->expects($this->once()) + ->method('isAutoReturnEnabled') + ->willReturn(false); + + $this->assertEquals( + $this->returnTOStock->afterExecute( + $this->refundInvoiceMock, + $creditmemoId, + $invoiceId, + $items, + false, + false, + false, + null, + $this->creditmemoCreationArgumentsMock + ), + $creditmemoId + ); + } +} diff --git a/app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/ReturnToStockOrderTest.php b/app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/ReturnToStockOrderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b4883dd1a7708a844e9c200162060fcac7cbf87a --- /dev/null +++ b/app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/ReturnToStockOrderTest.php @@ -0,0 +1,158 @@ +<?php +/** + * + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesInventory\Test\Unit\Model\Plugin\Order; + +use Magento\CatalogInventory\Api\StockConfigurationInterface; +use Magento\SalesInventory\Model\Order\ReturnProcessor; +use Magento\SalesInventory\Model\Plugin\Order\ReturnToStockOrder; +use Magento\Sales\Api\CreditmemoRepositoryInterface; +use Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface; +use Magento\Sales\Api\Data\CreditmemoCreationArgumentsExtensionInterface; +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Api\RefundOrderInterface; + +/** + * Class ReturnToStockOrderTest + */ +class ReturnToStockOrderTest extends \PHPUnit_Framework_TestCase +{ + /** @var ReturnToStockOrder */ + private $returnTOStock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|ReturnProcessor + */ + private $returnProcessorMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|CreditmemoRepositoryInterface + */ + private $creditmemoRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|OrderRepositoryInterface + */ + private $orderRepositoryMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|RefundOrderInterface + */ + private $refundOrderMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|CreditmemoCreationArgumentsInterface + */ + private $creditmemoCreationArgumentsMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|OrderInterface + */ + private $orderMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|CreditmemoInterface + */ + private $creditmemoMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|StockConfigurationInterface + */ + private $stockConfigurationMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|CreditmemoCreationArgumentsInterface + */ + private $extencionAttributesMock; + + protected function setUp() + { + $this->returnProcessorMock = $this->getMockBuilder(ReturnProcessor::class) + ->disableOriginalConstructor() + ->getMock(); + $this->creditmemoRepositoryMock = $this->getMockBuilder(CreditmemoRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->orderRepositoryMock = $this->getMockBuilder(OrderRepositoryInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->refundOrderMock = $this->getMockBuilder(RefundOrderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->creditmemoCreationArgumentsMock = $this->getMockBuilder(CreditmemoCreationArgumentsInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->extencionAttributesMock = $this->getMockBuilder(CreditmemoCreationArgumentsExtensionInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getReturnToStockItems']) + ->getMock(); + $this->orderMock = $this->getMockBuilder(OrderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->creditmemoMock = $this->getMockBuilder(CreditmemoInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $this->stockConfigurationMock = $this->getMockBuilder(StockConfigurationInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->returnTOStock = new ReturnToStockOrder( + $this->returnProcessorMock, + $this->creditmemoRepositoryMock, + $this->orderRepositoryMock, + $this->stockConfigurationMock + ); + } + + public function testAfterExecute() + { + $orderId = 1; + $creditmemoId = 99; + $items = []; + $returnToStockItems = [1]; + $this->creditmemoCreationArgumentsMock->expects($this->exactly(3)) + ->method('getExtensionAttributes') + ->willReturn($this->extencionAttributesMock); + + $this->extencionAttributesMock->expects($this->exactly(2)) + ->method('getReturnToStockItems') + ->willReturn($returnToStockItems); + + $this->orderRepositoryMock->expects($this->once()) + ->method('get') + ->with($orderId) + ->willReturn($this->orderMock); + + $this->creditmemoRepositoryMock->expects($this->once()) + ->method('get') + ->with($creditmemoId) + ->willReturn($this->creditmemoMock); + + $this->returnProcessorMock->expects($this->once()) + ->method('execute') + ->with($this->creditmemoMock, $this->orderMock, $returnToStockItems); + + $this->stockConfigurationMock->expects($this->once()) + ->method('isAutoReturnEnabled') + ->willReturn(false); + + $this->assertEquals( + $this->returnTOStock->afterExecute( + $this->refundOrderMock, + $creditmemoId, + $orderId, + $items, + false, + false, + null, + $this->creditmemoCreationArgumentsMock + ), + $creditmemoId + ); + } +} diff --git a/app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/Validation/InvoiceRefundCreationArgumentsTest.php b/app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/Validation/InvoiceRefundCreationArgumentsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d59da679d15e21331efcba024f05e0c5b0e05d4a --- /dev/null +++ b/app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/Validation/InvoiceRefundCreationArgumentsTest.php @@ -0,0 +1,150 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesInventory\Test\Unit\Model\Plugin\Order\Validation; + +use Magento\SalesInventory\Model\Order\ReturnValidator; +use Magento\SalesInventory\Model\Plugin\Order\Validation\InvoiceRefundCreationArguments; +use Magento\Sales\Model\ValidatorResultInterface; +use Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface; +use Magento\Sales\Api\Data\CreditmemoCreationArgumentsExtensionInterface; +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\Data\InvoiceInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\Order\Validation\RefundInvoiceInterface; + +/** + * Class InvoiceRefundCreatetionArgumentsTest + */ +class InvoiceRefundCreationArgumentsTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var InvoiceRefundCreationArguments + */ + private $plugin; + + /** + * @var ReturnValidator|\PHPUnit_Framework_MockObject_MockObject + */ + private $returnValidatorMock; + + /** + * @var CreditmemoCreationArgumentsExtensionInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $extencionAttributesMock; + + /** + * @var CreditmemoCreationArgumentsInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $creditmemoCreationArgumentsMock; + + /** + * @var RefundInvoiceInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $refundInvoiceValidatorMock; + + /** + * @var InvoiceInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $invoiceMock; + + /** + * @var ValidatorResultInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $validateResultMock; + + /** + * @var OrderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $orderMock; + + /** + * @var CreditmemoInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $creditmemoMock; + + protected function setUp() + { + $this->returnValidatorMock = $this->getMockBuilder(ReturnValidator::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->creditmemoCreationArgumentsMock = $this->getMockBuilder(CreditmemoCreationArgumentsInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->extencionAttributesMock = $this->getMockBuilder(CreditmemoCreationArgumentsExtensionInterface::class) + ->setMethods(['getReturnToStockItems']) + ->disableOriginalConstructor() + ->getMock(); + + $this->validateResultMock = $this->getMockBuilder(ValidatorResultInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->refundInvoiceValidatorMock = $this->getMockBuilder(RefundInvoiceInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->invoiceMock = $this->getMockBuilder(InvoiceInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->orderMock = $this->getMockBuilder(OrderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->creditmemoMock = $this->getMockBuilder(CreditmemoInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->plugin = new InvoiceRefundCreationArguments($this->returnValidatorMock); + } + + /** + * @dataProvider dataProvider + */ + public function testAfterValidation($erroMessage) + { + $returnToStockItems = [1]; + $this->creditmemoCreationArgumentsMock->expects($this->exactly(3)) + ->method('getExtensionAttributes') + ->willReturn($this->extencionAttributesMock); + + $this->extencionAttributesMock->expects($this->exactly(2)) + ->method('getReturnToStockItems') + ->willReturn($returnToStockItems); + + $this->returnValidatorMock->expects($this->once()) + ->method('validate') + ->willReturn($erroMessage); + + $this->validateResultMock->expects($erroMessage ? $this->once() : $this->never()) + ->method('addMessage') + ->with($erroMessage); + + $this->plugin->afterValidate( + $this->refundInvoiceValidatorMock, + $this->validateResultMock, + $this->invoiceMock, + $this->orderMock, + $this->creditmemoMock, + [], + false, + false, + false, + null, + $this->creditmemoCreationArgumentsMock + ); + } + + public function dataProvider() + { + return [ + 'withErrors' => ['Error!'], + 'withoutErrors' => ['null'], + ]; + } +} diff --git a/app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/Validation/OrderRefundCreationArgumentsTest.php b/app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/Validation/OrderRefundCreationArgumentsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6a6b88f3d4580df6cde0479297968e12291e8d5d --- /dev/null +++ b/app/code/Magento/SalesInventory/Test/Unit/Model/Plugin/Order/Validation/OrderRefundCreationArgumentsTest.php @@ -0,0 +1,141 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesInventory\Test\Unit\Model\Plugin\Order\Validation; + +use Magento\SalesInventory\Model\Order\ReturnValidator; +use Magento\SalesInventory\Model\Plugin\Order\Validation\OrderRefundCreationArguments; +use Magento\Sales\Model\Order\Validation\RefundOrderInterface; +use Magento\Sales\Model\ValidatorResultInterface; +use Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface; +use Magento\Sales\Api\Data\CreditmemoCreationArgumentsExtensionInterface; +use Magento\Sales\Api\Data\CreditmemoInterface; +use Magento\Sales\Api\Data\OrderInterface; + +/** + * Class OrderRefundCreatetionArgumentsTest + */ +class OrderRefundCreationArgumentsTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var OrderRefundCreationArguments + */ + private $plugin; + + /** + * @var ReturnValidator|\PHPUnit_Framework_MockObject_MockObject + */ + private $returnValidatorMock; + + /** + * @var CreditmemoCreationArgumentsExtensionInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $extencionAttributesMock; + + /** + * @var CreditmemoCreationArgumentsInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $creditmemoCreationArgumentsMock; + + /** + * @var RefundOrderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $refundOrderValidatorMock; + + /** + * @var ValidatorResultInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $validateResultMock; + + /** + * @var OrderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $orderMock; + + /** + * @var CreditmemoInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $creditmemoMock; + + protected function setUp() + { + $this->returnValidatorMock = $this->getMockBuilder(ReturnValidator::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->creditmemoCreationArgumentsMock = $this->getMockBuilder(CreditmemoCreationArgumentsInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->extencionAttributesMock = $this->getMockBuilder(CreditmemoCreationArgumentsExtensionInterface::class) + ->setMethods(['getReturnToStockItems']) + ->disableOriginalConstructor() + ->getMock(); + + $this->validateResultMock = $this->getMockBuilder(ValidatorResultInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->refundOrderValidatorMock = $this->getMockBuilder(RefundOrderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->orderMock = $this->getMockBuilder(OrderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->creditmemoMock = $this->getMockBuilder(CreditmemoInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->plugin = new OrderRefundCreationArguments($this->returnValidatorMock); + } + + /** + * @dataProvider dataProvider + */ + public function testAfterValidation($erroMessage) + { + $returnToStockItems = [1]; + $this->creditmemoCreationArgumentsMock->expects($this->exactly(3)) + ->method('getExtensionAttributes') + ->willReturn($this->extencionAttributesMock); + + $this->extencionAttributesMock->expects($this->exactly(2)) + ->method('getReturnToStockItems') + ->willReturn($returnToStockItems); + + $this->returnValidatorMock->expects($this->once()) + ->method('validate') + ->willReturn($erroMessage); + + $this->validateResultMock->expects($erroMessage ? $this->once() : $this->never()) + ->method('addMessage') + ->with($erroMessage); + + $this->plugin->afterValidate( + $this->refundOrderValidatorMock, + $this->validateResultMock, + $this->orderMock, + $this->creditmemoMock, + [], + false, + false, + null, + $this->creditmemoCreationArgumentsMock + ); + } + + /** + * @return array + */ + public function dataProvider() + { + return [ + 'withErrors' => ['Error!'], + 'withoutErrors' => ['null'], + ]; + } +} diff --git a/app/code/Magento/SalesInventory/composer.json b/app/code/Magento/SalesInventory/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..ff72ce7f0022690682b356235c506512e65497b9 --- /dev/null +++ b/app/code/Magento/SalesInventory/composer.json @@ -0,0 +1,26 @@ +{ + "name": "magento/module-sales-inventory", + "description": "N/A", + "require": { + "php": "~5.6.0|7.0.2|7.0.4|~7.0.6", + "magento/module-catalog-inventory": "100.2.*", + "magento/module-sales": "100.2.*", + "magento/module-store": "100.2.*", + "magento/module-catalog": "101.2.*", + "magento/framework": "100.2.*" + }, + "type": "magento2-module", + "version": "100.0.0-dev", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\SalesInventory\\": "" + } + } +} diff --git a/app/code/Magento/SalesInventory/etc/di.xml b/app/code/Magento/SalesInventory/etc/di.xml new file mode 100644 index 0000000000000000000000000000000000000000..aec39d3c51c29d398e7c52f45bf5cf1443d3bffa --- /dev/null +++ b/app/code/Magento/SalesInventory/etc/di.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Sales\Model\RefundOrder"> + <plugin name="refundOrderAfter" type="Magento\SalesInventory\Model\Plugin\Order\ReturnToStockOrder"/> + </type> + <type name="Magento\Sales\Model\RefundInvoice"> + <plugin name="refundInvoiceAfter" type="Magento\SalesInventory\Model\Plugin\Order\ReturnToStockInvoice"/> + </type> + <type name="Magento\Sales\Model\Order\Validation\RefundOrderInterface"> + <plugin name="refundOrderValidationAfter" type="Magento\SalesInventory\Model\Plugin\Order\Validation\OrderRefundCreationArguments"/> + </type> + <type name="Magento\Sales\Model\Order\Validation\RefundInvoiceInterface"> + <plugin name="refundInvoiceValidationAfter" type="Magento\SalesInventory\Model\Plugin\Order\Validation\InvoiceRefundCreationArguments"/> + </type> +</config> diff --git a/app/code/Magento/SalesInventory/etc/extension_attributes.xml b/app/code/Magento/SalesInventory/etc/extension_attributes.xml new file mode 100644 index 0000000000000000000000000000000000000000..de5b6d41c52621f6ba1b2bf432b912157431bc30 --- /dev/null +++ b/app/code/Magento/SalesInventory/etc/extension_attributes.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd"> + <extension_attributes for="Magento\Sales\Api\Data\CreditmemoCreationArgumentsInterface"> + <attribute code="return_to_stock_items" type="int[]"/> + </extension_attributes> +</config> \ No newline at end of file diff --git a/app/code/Magento/SalesInventory/etc/module.xml b/app/code/Magento/SalesInventory/etc/module.xml new file mode 100644 index 0000000000000000000000000000000000000000..06c9d0d78554afed0c4ab3e2d81bdeaea03c4df9 --- /dev/null +++ b/app/code/Magento/SalesInventory/etc/module.xml @@ -0,0 +1,17 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_SalesInventory" setup_version="1.0.0"> + <sequence> + <module name="Magento_Sales"/> + <module name="Magento_Catalog"/> + <module name="Magento_Store"/> + <module name="Magento_CatalogInventory"/> + </sequence> + </module> +</config> diff --git a/app/code/Magento/SalesInventory/registration.php b/app/code/Magento/SalesInventory/registration.php new file mode 100644 index 0000000000000000000000000000000000000000..edb96135508d2c61e5078ca771409a37a2c1bf30 --- /dev/null +++ b/app/code/Magento/SalesInventory/registration.php @@ -0,0 +1,11 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +\Magento\Framework\Component\ComponentRegistrar::register( + \Magento\Framework\Component\ComponentRegistrar::MODULE, + 'Magento_SalesInventory', + __DIR__ +); diff --git a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php index d265159bc630be220bc4a7a64b706e36786510ef..1c59c4033d377996da9d7b36dc5c9b0b6ba02fe8 100644 --- a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php +++ b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php @@ -107,6 +107,8 @@ class Save extends \Magento\Backend\App\Action $this->_objectManager->get(\Magento\Backend\Model\Session::class)->setCommentText($data['comment_text']); } + $isNeedCreateLabel = isset($data['create_shipping_label']) && $data['create_shipping_label']; + try { $this->shipmentLoader->setOrderId($this->getRequest()->getParam('order_id')); $this->shipmentLoader->setShipmentId($this->getRequest()->getParam('shipment_id')); @@ -128,10 +130,12 @@ class Save extends \Magento\Backend\App\Action $shipment->setCustomerNote($data['comment_text']); $shipment->setCustomerNoteNotify(isset($data['comment_customer_notify'])); } - $errorMessages = $this->getShipmentValidator()->validate($shipment, [QuantityValidator::class]); - if (!empty($errorMessages)) { + $validationResult = $this->getShipmentValidator() + ->validate($shipment, [QuantityValidator::class]); + + if ($validationResult->hasMessages()) { $this->messageManager->addError( - __("Shipment Document Validation Error(s):\n" . implode("\n", $errorMessages)) + __("Shipment Document Validation Error(s):\n" . implode("\n", $validationResult->getMessages())) ); $this->_redirect('*/*/new', ['order_id' => $this->getRequest()->getParam('order_id')]); return; @@ -140,7 +144,6 @@ class Save extends \Magento\Backend\App\Action $shipment->getOrder()->setCustomerNoteNotify(!empty($data['send_email'])); $responseAjax = new \Magento\Framework\DataObject(); - $isNeedCreateLabel = isset($data['create_shipping_label']) && $data['create_shipping_label']; if ($isNeedCreateLabel) { $this->labelGenerator->create($shipment, $this->_request); diff --git a/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php b/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php index f8d9c06dc8ec05c07c699c4b22e7b830c796e4dc..b1d2fc0d1d74456593b4cb4e3248e965b2033958 100644 --- a/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php +++ b/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php @@ -9,6 +9,7 @@ namespace Magento\Shipping\Test\Unit\Controller\Adminhtml\Order\Shipment; use Magento\Backend\App\Action; +use Magento\Sales\Model\ValidatorResultInterface; use Magento\Sales\Model\Order\Email\Sender\ShipmentSender; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface; @@ -18,6 +19,7 @@ use Magento\Sales\Model\Order\Shipment\Validation\QuantityValidator; * Class SaveTest * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyFields) */ class SaveTest extends \PHPUnit_Framework_TestCase { @@ -96,6 +98,11 @@ class SaveTest extends \PHPUnit_Framework_TestCase */ private $shipmentValidatorMock; + /** + * @var ValidatorResultInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $validationResult; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -107,6 +114,9 @@ class SaveTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->setMethods([]) ->getMock(); + $this->validationResult = $this->getMockBuilder(ValidatorResultInterface::class) + ->disableOriginalConstructor() + ->getMock(); $this->labelGenerator = $this->getMockBuilder(\Magento\Shipping\Model\Shipping\LabelGenerator::class) ->disableOriginalConstructor() ->setMethods([]) @@ -362,7 +372,11 @@ class SaveTest extends \PHPUnit_Framework_TestCase $this->shipmentValidatorMock->expects($this->once()) ->method('validate') ->with($shipment, [QuantityValidator::class]) - ->willReturn([]); + ->willReturn($this->validationResult); + + $this->validationResult->expects($this->once()) + ->method('hasMessages') + ->willReturn(false); $this->saveAction->execute(); $this->assertEquals($this->response, $this->saveAction->getResponse()); diff --git a/app/etc/di.xml b/app/etc/di.xml index 28f0d024c3ae234583e19b31800c10c950a5b42d..643390068a5e3e857ccb0e0d2d07fb5e26b8ea81 100755 --- a/app/etc/di.xml +++ b/app/etc/di.xml @@ -1205,7 +1205,6 @@ <arguments> <argument name="mappers" xsi:type="array"> <item name="mapper" xsi:type="object">Magento\Framework\EntityManager\Mapper</item> - <item name="customAttributesMapper" xsi:type="object">Magento\Framework\EntityManager\CustomAttributesMapper</item> </argument> </arguments> </type> diff --git a/composer.json b/composer.json index 63d02e7c0a8b47dcadcd7444faa17a2d0435950c..fed4465140724cef3c24cd7facdd2ec5ceeba5b6 100644 --- a/composer.json +++ b/composer.json @@ -151,6 +151,7 @@ "magento/module-rss": "100.2.0-dev", "magento/module-rule": "100.2.0-dev", "magento/module-sales": "100.2.0-dev", + "magento/module-sales-inventory": "100.0.0-dev", "magento/module-sales-rule": "100.2.0-dev", "magento/module-sales-sequence": "100.2.0-dev", "magento/module-sample-data": "100.2.0-dev", diff --git a/composer.lock b/composer.lock index 54a2b572c024bda51e7c7c2458fab8c45e2acb37..7feb568d766d012e009c59a50226f3ea8bd7b74a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "0cd85c424e865c554b2f8db093192cfd", - "content-hash": "d74ec028da2018e1a161a2e1b70d3f87", + "hash": "c23e80be1cc71ab108ce5ac19b3fe509", + "content-hash": "5b9734c1bdbda68cf20507525cafa0f2", "packages": [ { "name": "braintree/braintree_php", @@ -342,16 +342,16 @@ }, { "name": "composer/spdx-licenses", - "version": "1.1.4", + "version": "1.1.5", "source": { "type": "git", "url": "https://github.com/composer/spdx-licenses.git", - "reference": "88c26372b1afac36d8db601cdf04ad8716f53d88" + "reference": "96c6a07b05b716e89a44529d060bc7f5c263cb13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/88c26372b1afac36d8db601cdf04ad8716f53d88", - "reference": "88c26372b1afac36d8db601cdf04ad8716f53d88", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/96c6a07b05b716e89a44529d060bc7f5c263cb13", + "reference": "96c6a07b05b716e89a44529d060bc7f5c263cb13", "shasum": "" }, "require": { @@ -399,7 +399,7 @@ "spdx", "validator" ], - "time": "2016-05-04 12:27:30" + "time": "2016-09-28 07:17:45" }, { "name": "justinrainbow/json-schema", @@ -3242,16 +3242,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v1.12.1", + "version": "v1.12.2", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "d33ee60f3d3e6152888b7f3a385f49e5c43bf1bf" + "reference": "baa7112bef3b86c65fcfaae9a7a50436e3902b41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/d33ee60f3d3e6152888b7f3a385f49e5c43bf1bf", - "reference": "d33ee60f3d3e6152888b7f3a385f49e5c43bf1bf", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/baa7112bef3b86c65fcfaae9a7a50436e3902b41", + "reference": "baa7112bef3b86c65fcfaae9a7a50436e3902b41", "shasum": "" }, "require": { @@ -3296,7 +3296,7 @@ } ], "description": "A tool to automatically fix PHP code style", - "time": "2016-09-07 06:48:24" + "time": "2016-09-27 07:57:59" }, { "name": "lusitanian/oauth", diff --git a/dev/tests/api-functional/testsuite/Magento/SalesInventory/Api/Service/V1/ReturnItemsAfterRefundOrderTest.php b/dev/tests/api-functional/testsuite/Magento/SalesInventory/Api/Service/V1/ReturnItemsAfterRefundOrderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..68ecebec27c0b0812d76eb7be8c2ac063aff54ef --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/SalesInventory/Api/Service/V1/ReturnItemsAfterRefundOrderTest.php @@ -0,0 +1,114 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesInventory\Api\Service\V1; + +/** + * API test for return items to stock + */ +class ReturnItemsAfterRefundOrderTest extends \Magento\TestFramework\TestCase\WebapiAbstract +{ + const SERVICE_REFUND_ORDER_NAME = 'salesRefundOrderV1'; + const SERVICE_STOCK_ITEMS_NAME = 'stockItems'; + + /** + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + } + + /** + * @dataProvider dataProvider + * @magentoApiDataFixture Magento/Sales/_files/order_with_shipping_and_invoice.php + */ + public function testRefundWithReturnItemsToStock($qtyRefund) + { + $productSku = 'simple'; + /** @var \Magento\Sales\Model\Order $existingOrder */ + $existingOrder = $this->objectManager->create(\Magento\Sales\Model\Order::class) + ->loadByIncrementId('100000001'); + $orderItems = $existingOrder->getItems(); + $orderItem = array_shift($orderItems); + $expectedItems = [['order_item_id' => $orderItem->getItemId(), 'qty' => $qtyRefund]]; + $qtyBeforeRefund = $this->getQtyInStockBySku($productSku); + + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/order/' . $existingOrder->getEntityId() . '/refund', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + ], + 'soap' => [ + 'service' => self::SERVICE_REFUND_ORDER_NAME, + 'serviceVersion' => 'V1', + 'operation' => self::SERVICE_REFUND_ORDER_NAME . 'execute', + ] + ]; + + $this->_webApiCall( + $serviceInfo, + [ + 'orderId' => $existingOrder->getEntityId(), + 'items' => $expectedItems, + 'arguments' => [ + 'extension_attributes' => [ + 'return_to_stock_items' => [ + (int)$orderItem->getItemId() + ], + ], + ], + ] + ); + + $qtyAfterRefund = $this->getQtyInStockBySku($productSku); + + try { + $this->assertEquals( + $qtyBeforeRefund + $expectedItems[0]['qty'], + $qtyAfterRefund, + 'Failed asserting qty of returned items incorrect.' + ); + + } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + $this->fail('Failed asserting that Creditmemo was created'); + } + } + + /** + * @return array + */ + public function dataProvider() + { + return [ + 'refundAllOrderItems' => [2], + 'refundPartition' => [1], + ]; + } + + /** + * @param string $sku + * @return int + */ + private function getQtyInStockBySku($sku) + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/' . self::SERVICE_STOCK_ITEMS_NAME . "/$sku", + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + ], + 'soap' => [ + 'service' => 'catalogInventoryStockRegistryV1', + 'serviceVersion' => 'V1', + 'operation' => 'catalogInventoryStockRegistryV1GetStockItemBySku', + ], + ]; + $arguments = ['productSku' => $sku]; + $apiResult = $this->_webApiCall($serviceInfo, $arguments); + return $apiResult['qty']; + } +} diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Setup.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Setup.php new file mode 100644 index 0000000000000000000000000000000000000000..ceb84eb46acc97c590440e2f7f5e6031ddc969b4 --- /dev/null +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli/Setup.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Mtf\Util\Command\Cli; + +use Magento\Mtf\Util\Command\Cli; + +/** + * Setup Magento for tests executions. + */ +class Setup extends Cli +{ + /** + * Parameter for uninstall Magento command. + */ + const PARAM_SETUP_UNINSTALL = 'setup:uninstall'; + + /** + * Options for uninstall Magento command. + * + * @var array + */ + private $options = ['-n']; + + /** + * Uninstall Magento. + * + * @return void + */ + public function uninstall() + { + parent::execute(Setup::PARAM_SETUP_UNINSTALL, $this->options); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/Attribute.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/Attribute.php deleted file mode 100644 index ca29475c03c90181f7caa0849e11a7639b023158..0000000000000000000000000000000000000000 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/Attribute.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Catalog\Test\Block\Adminhtml\Product\Edit\Action; - -use Magento\Mtf\Fixture\FixtureInterface; -use Magento\Mtf\Block\Form; -use Magento\Mtf\Client\Element\SimpleElement; - -/** - * Product attribute massaction edit page. - */ -class Attribute extends Form -{ - /** - * Fill the root form. - * - * @param FixtureInterface $fixture - * @param SimpleElement|null $element - * @return $this - */ - public function fill(FixtureInterface $fixture, SimpleElement $element = null) - { - $data = $fixture->getData(); - $fields = []; - foreach ($data as $name => $dataValue) { - $fields['toggle_' . $name] = 'Yes'; - $fields[$name] = $dataValue; - } - $mapping = $this->dataMapping($fields); - $this->_fill($mapping, $element); - - return $this; - } -} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/Attribute.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/Attribute.xml deleted file mode 100644 index 59b367e49bd66b8dce3cfb1e25dc4b46f9e2eff8..0000000000000000000000000000000000000000 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/Attribute.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" ?> -<!-- -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<mapping strict="0"> - <wrapper>attributes</wrapper> - <fields> - <toggle_price> - <selector>[name='toggle_price']</selector> - <input>checkbox</input> - </toggle_price> - <price /> - </fields> -</mapping> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/Tab/UpdateAttributeTab.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/Tab/UpdateAttributeTab.php new file mode 100644 index 0000000000000000000000000000000000000000..d61c2001a2096ebaedd895a0c483d5a93a9ae8bd --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/Tab/UpdateAttributeTab.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Block\Adminhtml\Product\Edit\Action\Tab; + +use Magento\Mtf\Client\Element\SimpleElement; +use Magento\Mtf\Client\Locator; +use Magento\Backend\Test\Block\Widget\Tab; + +/** + * Tab on Product update attributes Form. + */ +class UpdateAttributeTab extends Tab +{ + /** + * Change checkbox. + * + * @var string + */ + private $changeCheckbox = [ + 'selector' => './/./ancestor::div[contains(@class,"control")]' + . '//input[@data-role="toggle-editability-all" or contains(@id, "toggle_")]', + 'strategy' => Locator::SELECTOR_XPATH, + 'input' => 'checkbox', + 'value' => 'Yes', + ]; + + /** + * Fill data into fields in the container. + * + * @param array $fields + * @param SimpleElement|null $contextElement + * @return $this + */ + public function setFieldsData(array $fields, SimpleElement $contextElement = null) + { + $context = ($contextElement === null) ? $this->_rootElement : $contextElement; + $mapping = $this->dataMapping($fields); + foreach ($mapping as $field) { + $this->_fill([$this->changeCheckbox], $context->find($field['selector'], $field['strategy'])); + $this->_fill([$field], $context); + } + + return $this; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/UpdateAttributeForm.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/UpdateAttributeForm.php new file mode 100644 index 0000000000000000000000000000000000000000..a264c491de43aa6fdfd22eb98793316e79996054 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/UpdateAttributeForm.php @@ -0,0 +1,17 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Block\Adminhtml\Product\Edit\Action; + +use Magento\Backend\Test\Block\Widget\FormTabs; + +/** + * Product update Attributes Form. + */ +class UpdateAttributeForm extends FormTabs +{ + // +} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/UpdateAttributeForm.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/UpdateAttributeForm.xml new file mode 100644 index 0000000000000000000000000000000000000000..ba8effd797a3dc85e92601fd7037815ef02c91a9 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Action/UpdateAttributeForm.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" ?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tabs> + <product-details> + <class>Magento\Catalog\Test\Block\Adminhtml\Product\Edit\Action\Tab\UpdateAttributeTab</class> + <selector>#attributes_update_tabs_attributes</selector> + <fields> + <price> + <selector>#price</selector> + </price> + </fields> + </product-details> + <advanced-inventory> + <class>Magento\Catalog\Test\Block\Adminhtml\Product\Edit\Action\Tab\UpdateAttributeTab</class> + <selector>#attributes_update_tabs_inventory</selector> + <fields> + <stock_data> + <selector>#inventory_stock_availability</selector> + <input>select</input> + </stock_data> + </fields> + </advanced-inventory> +</tabs> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Grid.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Grid.php index d5e81ad56fd3290ef906c8cb141da8e583757c24..db73f7cc87f177fb1eb05f0184b238cb82cd47f1 100755 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Grid.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Grid.php @@ -7,6 +7,7 @@ namespace Magento\Catalog\Test\Block\Adminhtml\Product; use Magento\Ui\Test\Block\Adminhtml\DataGrid; +use Magento\Mtf\Fixture\FixtureInterface; /** * Backend catalog product grid. @@ -72,12 +73,17 @@ class Grid extends DataGrid /** * Update attributes for selected items. * - * @param array $items [optional] + * @param array $items * @return void */ public function updateAttributes(array $items = []) { - $this->massaction($items, 'Update attributes'); + $products = []; + /** @var FixtureInterface $product */ + foreach ($items as $product) { + $products[] = ["sku" => $product->getSku()]; + } + $this->massaction($products, 'Update attributes'); } /** diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertMassProductUpdateSuccessMessage.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertMassProductUpdateSuccessMessage.php index 1666244802ed2e81c5375c2d41b0df0dff0a0191..df326a9ca0023a3c3fbeb45a2c960e88d9fffc32 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertMassProductUpdateSuccessMessage.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertMassProductUpdateSuccessMessage.php @@ -23,13 +23,12 @@ class AssertMassProductUpdateSuccessMessage extends AbstractConstraint * Assert that after mass update successful message appears. * * @param CatalogProductIndex $productGrid - * @param array $products + * @param int $productsCount * @return void */ - public function processAssert(CatalogProductIndex $productGrid, $products = []) + public function processAssert(CatalogProductIndex $productGrid, $productsCount) { - $countProducts = count($products) ? count($products) : 1; - $expectedMessage = sprintf(self::SUCCESS_MESSAGE, $countProducts); + $expectedMessage = sprintf(self::SUCCESS_MESSAGE, $productsCount); $actualMessage = $productGrid->getMessagesBlock()->getSuccessMessage(); \PHPUnit_Framework_Assert::assertEquals( $expectedMessage, diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductsInStock.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductsInStock.php new file mode 100644 index 0000000000000000000000000000000000000000..f485076d5c018b97dfc67624d505bb617929b901 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductsInStock.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Constraint; + +use Magento\Catalog\Test\Page\Product\CatalogProductView; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert that all products are in stock. + */ +class AssertProductsInStock extends AbstractConstraint +{ + /** + * Assert that In Stock status is displayed for products. + * + * @param CatalogProductView $catalogProductView + * @param BrowserInterface $browser + * @param AssertProductInStock $assertProductInStock + * @param array $products + * @return void + */ + public function processAssert( + CatalogProductView $catalogProductView, + BrowserInterface $browser, + AssertProductInStock $assertProductInStock, + array $products + ) { + foreach ($products as $product) { + $assertProductInStock->processAssert($catalogProductView, $browser, $product); + } + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'In stock control is visible for each product.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Page/Adminhtml/CatalogProductActionAttributeEdit.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Page/Adminhtml/CatalogProductActionAttributeEdit.xml index e875bdc0817e9bcd97b4c933de90ede7498aec7a..1ff06177a5a20b716f1a24201a785c1ff12eecd6 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Page/Adminhtml/CatalogProductActionAttributeEdit.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Page/Adminhtml/CatalogProductActionAttributeEdit.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/pages.xsd"> <page name="CatalogProductActionAttributeEdit" area="Adminhtml" mca="catalog/product_action_attribute/edit" module="Magento_Catalog"> - <block name="attributesBlockForm" class="Magento\Catalog\Test\Block\Adminhtml\Product\Edit\Action\Attribute" locator="body" strategy="css selector" /> + <block name="attributesBlockForm" class="Magento\Catalog\Test\Block\Adminhtml\Product\Edit\Action\UpdateAttributeForm" locator="body" strategy="css selector" /> <block name="formPageActions" class="Magento\Catalog\Test\Block\Adminhtml\Product\Edit\Action\FormPageActions" locator=".page-main-actions" strategy="css selector" /> </page> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/TierPrice.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/TierPrice.xml index bdfb5deabe5c2828b947130284f0308353fa6bdc..412818cbe40e71634eb1e635d8dc8e1630913b49 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/TierPrice.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/TierPrice.xml @@ -66,5 +66,16 @@ </item> </field> </dataset> + + <dataset name="not_logged_in"> + <field name="0" xsi:type="array"> + <item name="price" xsi:type="string">90</item> + <item name="website" xsi:type="string">All Websites [USD]</item> + <item name="price_qty" xsi:type="string">2</item> + <item name="customer_group" xsi:type="array"> + <item name="dataset" xsi:type="string">NOT_LOGGED_IN</item> + </item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityTest.xml index 77d4ee802e1373570a77f5e136c18c5ba393ae4d..9e410c525deef720573e7c93e0de4d463b9b2ac7 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityTest.xml @@ -168,7 +168,7 @@ <data name="product/data/url_key" xsi:type="string">simple-product-%isolation%</data> <data name="product/data/name" xsi:type="string">Simple Product %isolation%</data> <data name="product/data/sku" xsi:type="string">simple_sku_%isolation%</data> - <data name="product/data/price/value" xsi:type="string">10008</data> + <data name="product/data/price/value" xsi:type="string">10008.88</data> <data name="product/data/short_description" xsi:type="string">Simple Product short_description %isolation%</data> <data name="product/data/description" xsi:type="string">Simple Product description %isolation%</data> <data name="product/data/weight" xsi:type="string">58</data> @@ -461,6 +461,28 @@ <data name="product/data/country_of_manufacture" xsi:type="string">Antarctica</data> <constraint name="Magento\Catalog\Test\Constraint\AssertProductSaveMessage" /> </variation> + <variation name="CreateSimpleProductEntityTestVariation28" summary="Create product with tier price for not logged in customer"> + <data name="product/data/url_key" xsi:type="string">simple-product-%isolation%</data> + <data name="product/data/name" xsi:type="string">Simple Product %isolation%</data> + <data name="product/data/sku" xsi:type="string">simple_sku_%isolation%</data> + <data name="product/data/price/value" xsi:type="string">100</data> + <data name="product/data/weight" xsi:type="string">50</data> + <data name="product/data/quantity_and_stock_status/qty" xsi:type="string">667</data> + <data name="product/data/tier_price/dataset" xsi:type="string">not_logged_in</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductSaveMessage" /> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductTierPriceOnProductPage" /> + </variation> + <variation name="CreateSimpleProductEntityTestVariation29" summary="Create Simple Product and assign it to custom website"> + <data name="product/data/url_key" xsi:type="string">simple-product-%isolation%</data> + <data name="product/data/name" xsi:type="string">Simple Product %isolation%</data> + <data name="product/data/sku" xsi:type="string">simple_sku_%isolation%</data> + <data name="product/data/price/value" xsi:type="string">200.20</data> + <data name="product/data/weight" xsi:type="string">50</data> + <data name="product/data/quantity_and_stock_status/qty" xsi:type="string">668</data> + <data name="product/data/website_ids/0/dataset" xsi:type="string">custom_store</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductSaveMessage" /> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductOnCustomWebsite" /> + </variation> <variation name="CreateSimpleProductEntityWithEmptySkuMaskTest1" summary="Create Simple Product Entity With Empty Sku Mask" ticketId="MAGETWO-58951"> <data name="configData" xsi:type="string">empty_product_mask_sku</data> <data name="description" xsi:type="string">Create product with custom options(fixed price)</data> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateTest.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateTest.php index f7fd82e3342bfdce8760c253215ad8189ef33643..9c38ff1b479fe6a02f2217572bea170b1906f00d 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateTest.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateTest.php @@ -6,10 +6,13 @@ namespace Magento\Catalog\Test\TestCase\Product; +use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Mtf\Fixture\FixtureInterface; use Magento\Mtf\TestCase\Injectable; use Magento\Catalog\Test\Fixture\CatalogProductSimple; use Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex; use Magento\Catalog\Test\Page\Adminhtml\CatalogProductActionAttributeEdit; +use Magento\Mtf\TestStep\TestStepFactory; /** * Precondition: @@ -58,35 +61,58 @@ class MassProductUpdateTest extends Injectable */ protected $configData; + /** + * Factory for Test Steps. + * + * @var TestStepFactory + */ + private $testStepFactory; + + /** + * Factory for Fixtures. + * + * @var FixtureFactory + */ + private $fixtureFactory; + /** * Injection data. * * @param CatalogProductIndex $productGrid * @param CatalogProductActionAttributeEdit $attributeMassActionPage + * @param TestStepFactory $testStepFactory + * @param FixtureFactory $fixtureFactory * @return void */ public function __inject( CatalogProductIndex $productGrid, - CatalogProductActionAttributeEdit $attributeMassActionPage + CatalogProductActionAttributeEdit $attributeMassActionPage, + TestStepFactory $testStepFactory, + FixtureFactory $fixtureFactory ) { $this->productGrid = $productGrid; $this->attributeMassActionPage = $attributeMassActionPage; + $this->testStepFactory = $testStepFactory; + $this->fixtureFactory = $fixtureFactory; } /** * Run mass update product simple entity test. * - * @param CatalogProductSimple $initialProduct * @param CatalogProductSimple $product * @param string $configData + * @param array $initialProducts * @return array */ - public function test(CatalogProductSimple $initialProduct, CatalogProductSimple $product, $configData) + public function test(CatalogProductSimple $product, $configData, array $initialProducts) { $this->configData = $configData; // Preconditions - $initialProduct->persist(); + $products = $this->testStepFactory->create( + \Magento\Catalog\Test\TestStep\CreateProductsStep::class, + ['products' => $initialProducts] + )->run()['products']; $this->objectManager->create( \Magento\Config\Test\TestStep\SetupConfigurationStep::class, @@ -95,19 +121,33 @@ class MassProductUpdateTest extends Injectable // Steps $this->productGrid->open(); - $this->productGrid->getProductGrid()->updateAttributes([['sku' => $initialProduct->getSku()]]); + $this->productGrid->getProductGrid()->updateAttributes($products); $this->attributeMassActionPage->getAttributesBlockForm()->fill($product); $this->attributeMassActionPage->getFormPageActions()->save(); - $data = array_merge($initialProduct->getData(), $product->getData()); - $product = $this->objectManager->create( - \Magento\Catalog\Test\Fixture\CatalogProductSimple::class, - ['data' => $data] - ); - - return [ - 'category' => $initialProduct->getDataFieldConfig('category_ids')['source']->getCategories()[0], - 'product' => $product, - ]; + $updatedProducts = $this->prepareUpdatedProducts($products, $product); + + return ['products' => $updatedProducts]; + } + + /** + * Prepare updated products. + * + * @param array $products + * @param CatalogProductSimple $product + * @return array + */ + private function prepareUpdatedProducts(array $products, CatalogProductSimple $product) + { + $productsReturn = []; + /** @var FixtureInterface $item */ + foreach ($products as $item) { + $productsReturn[] = $this->fixtureFactory->create( + get_class($item), + ['data' => array_merge($item->getData(), $product->getData())] + ); + } + + return $productsReturn; } /** diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateTest.xml index 6f937999f3c34cc172b3552b6280b7563d07a1d1..5435ea89669f6f1a3d0d3d98cb8e1a5bb5aabed0 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/MassProductUpdateTest.xml @@ -9,11 +9,12 @@ <testCase name="Magento\Catalog\Test\TestCase\Product\MassProductUpdateTest" summary="Edit Products Using Mass Actions" ticketId="MAGETWO-21128"> <variation name="MassProductPriceUpdateTestVariation1"> <data name="configData" xsi:type="string">product_flat</data> - <data name="initialProduct/dataset" xsi:type="string">simple_10_dollar</data> + <data name="initialProducts/0" xsi:type="string">catalogProductSimple::simple_10_dollar</data> + <data name="initialProducts/1" xsi:type="string">catalogProductSimple::simple_10_dollar</data> + <data name="productsCount" xsi:type="number">2</data> <data name="product/data/price/value" xsi:type="string">1.99</data> <constraint name="Magento\Catalog\Test\Constraint\AssertMassProductUpdateSuccessMessage" /> <constraint name="Magento\Catalog\Test\Constraint\AssertProductInGrid" /> - <constraint name="Magento\Catalog\Test\Constraint\AssertProductInCategory" /> </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.xml index 323246949d1a55825bfe49317af6662be585d15c..0395f9f5ed8cf83c0aa570252cc5ed7df32da3c8 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/UpdateSimpleProductEntityTest.xml @@ -145,5 +145,19 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductInGrid" /> <constraint name="Magento\Catalog\Test\Constraint\AssertProductInCategory" /> </variation> + <variation name="EditSimpleProductTestVariation11" summary="Update simple product with custom option"> + <data name="initialProduct/dataset" xsi:type="string">product_with_category</data> + <data name="product/data/name" xsi:type="string">Test simple product %isolation%</data> + <data name="product/data/sku" xsi:type="string">test_simple_product_%isolation%</data> + <data name="product/data/price/value" xsi:type="string">245.00</data> + <data name="product/data/quantity_and_stock_status/qty" xsi:type="string">200</data> + <data name="product/data/custom_options/dataset" xsi:type="string">drop_down_with_one_option_percent_price</data> + <data name="product/data/checkout_data/dataset" xsi:type="string">simple_drop_down_with_one_option_percent_price</data> + <data name="product/data/url_key" xsi:type="string">test-simple-product-%isolation%</data> + <data name="product/data/weight" xsi:type="string">120.0000</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductSaveMessage" /> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductForm" /> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductInCart" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart.php index e8178d07a1e9e26b6494d0b06551149636e4e4c1..697733fd3c33d8856f5d775452aca8ad977e77e9 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart.php @@ -129,7 +129,7 @@ class Cart extends Block Locator::SELECTOR_XPATH ); $cartItem = $this->blockFactory->create( - \Magento\Checkout\Test\Block\Cart\CartItem::class, + '\\' . get_class($this) . '\CartItem', ['element' => $cartItemBlock] ); } diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Totals.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Totals.php index 2cf6d5ad87dfff1d9f5c96b9ed40414abb822d44..240ae1c183201f2c479564b5dfad59a274e84b9d 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Totals.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Cart/Totals.php @@ -240,7 +240,7 @@ class Totals extends Block */ public function isVisibleShippingPriceBlock() { - return $this->_rootElement->find($this->shippingPriceBlockSelector, Locator::SELECTOR_CSS)->isVisible(); + return $this->_rootElement->find($this->shippingPriceBlockSelector, Locator::SELECTOR_CSS)->isVisible(); } /** diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Block/Adminhtml/Product/Grid.php b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Block/Adminhtml/Product/Grid.php new file mode 100644 index 0000000000000000000000000000000000000000..e6fa3271ea6cd60d1f8ae7e509a3c873344e7e7d --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Block/Adminhtml/Product/Grid.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\ConfigurableProduct\Test\Block\Adminhtml\Product; + +use Magento\ConfigurableProduct\Test\Fixture\ConfigurableProduct; + +/** + * Backend catalog product grid. + */ +class Grid extends \Magento\Catalog\Test\Block\Adminhtml\Product\Grid +{ + /** + * Prepare data. + * + * @param ConfigurableProduct $product + * @return array + */ + public function prepareData($product) + { + $configurableAttributesData = $product->getConfigurableAttributesData(); + $productItems[] = ['sku' => $product->getSku()]; + foreach ($configurableAttributesData['matrix'] as $variation) { + $productItems[] = ['sku' => $variation['sku']]; + } + + return $productItems; + } +} 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 2a3703a10607554c2682f19b036201cc700dc477..4a69a7604bca3d74119b07bdcea3b2115a1f6648 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 @@ -41,6 +41,35 @@ </field> </dataset> + <dataset name="out_of_stock"> + <field name="name" xsi:type="string">Test configurable product %isolation%</field> + <field name="sku" xsi:type="string">sku_test_configurable_product_%isolation%</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> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">configurable-product-%isolation%</field> + <field name="configurable_attributes_data" xsi:type="array"> + <item name="dataset" xsi:type="string">two_options_with_assigned_product</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">Out of Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + </dataset> + <dataset name="configurable_with_qty_1"> <field name="name" xsi:type="string">Test configurable product %isolation%</field> <field name="sku" xsi:type="string">sku_test_configurable_product_%isolation%</field> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/MassProductUpdateTest.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/MassProductUpdateTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..3d58645d618ffe4f34beb84cd3a1d58212649148 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/MassProductUpdateTest.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Catalog\Test\TestCase\Product\MassProductUpdateTest" summary="Edit Products Using Mass Actions" ticketId="MAGETWO-21128"> + <variation name="MassProductUpdateTestVariation2" summary="Update stock data for simple and configurable"> + <data name="configData" xsi:type="string">product_flat</data> + <data name="initialProducts/1" xsi:type="string">configurableProduct::out_of_stock</data> + <data name="initialProducts/0" xsi:type="string">catalogProductSimple::out_of_stock</data> + <data name="productsCount" xsi:type="number">2</data> + <data name="product/data/stock_data" xsi:type="string">In Stock</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertMassProductUpdateSuccessMessage" /> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductInGrid" /> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductsInStock" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Install/Test/Block/Devdocs.php b/dev/tests/functional/tests/app/Magento/Install/Test/Block/Devdocs.php new file mode 100644 index 0000000000000000000000000000000000000000..1be88c0bbf7b89f7dbf529456a9ee1235df1e163 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Install/Test/Block/Devdocs.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Install\Test\Block; + +use Magento\Mtf\Block\Block; + +/** + * Developer Documentation block. + */ +class Devdocs extends Block +{ + /** + * Developer Documentation title. + * + * @var string + */ + protected $devdocsTitle = '.page-heading'; + + /** + * Get Developer Documentation title text. + * + * @return string + */ + public function getDevdocsTitle() + { + return $this->_rootElement->find($this->devdocsTitle)->getText(); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Install/Test/Block/Landing.php b/dev/tests/functional/tests/app/Magento/Install/Test/Block/Landing.php index 5b92b332b26960fdfc0e285459e39b78f5c72f67..d809fb861b66c92138a3231aeae8a7bd35ea15d9 100644 --- a/dev/tests/functional/tests/app/Magento/Install/Test/Block/Landing.php +++ b/dev/tests/functional/tests/app/Magento/Install/Test/Block/Landing.php @@ -14,6 +14,13 @@ use Magento\Mtf\Client\Locator; */ class Landing extends Block { + /** + * Link by text. + * + * @var string + */ + protected $linkSelector = '//a[text()="%s"]'; + /** * 'Agree and Set up Magento' button. * @@ -47,4 +54,15 @@ class Landing extends Block { $this->_rootElement->find($this->termsAndAgreement, Locator::SELECTOR_CSS)->click(); } + + /** + * Click on link. + * + * @param string $text + * @return void + */ + public function clickLink($text) + { + $this->_rootElement->find(sprintf($this->linkSelector, $text), Locator::SELECTOR_XPATH)->click(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Install/Test/Constraint/AssertDevdocsLink.php b/dev/tests/functional/tests/app/Magento/Install/Test/Constraint/AssertDevdocsLink.php new file mode 100644 index 0000000000000000000000000000000000000000..d397cc1882b6f39c2fa5423a9ccecbceec002aaa --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Install/Test/Constraint/AssertDevdocsLink.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Install\Test\Constraint; + +use Magento\Install\Test\Page\DevdocsInstall; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Check Developer Documentation link. + */ +class AssertDevdocsLink extends AbstractConstraint +{ + /** + * Developer Documentation title. + */ + const DEVDOCS_TITLE_TEXT = 'Setup Wizard installation'; + + /** + * Check Developer Documentation link. + * + * @param DevdocsInstall $devdocsInstallPage + * @return void + */ + public function processAssert(DevdocsInstall $devdocsInstallPage) + { + \PHPUnit_Framework_Assert::assertEquals( + self::DEVDOCS_TITLE_TEXT, + $devdocsInstallPage->getDevdocsBlock()->getDevdocsTitle(), + 'Developer Documentation link is wrong.' + ); + } + + /** + * Returns a string representation of successful assertion. + * + * @return string + */ + public function toString() + { + return "Developer Documentation link is correct."; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Install/Test/Page/DevdocsInstall.xml b/dev/tests/functional/tests/app/Magento/Install/Test/Page/DevdocsInstall.xml new file mode 100644 index 0000000000000000000000000000000000000000..d2dc975cc2b2bd82a72b6b9e4e1d71b97fd635b4 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Install/Test/Page/DevdocsInstall.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/pages.xsd"> + <page name="DevdocsInstall" mca="http://devdocs.magento.com/guides/v2.0/install-gde/install/web/install-web.html" module="Magento_Install"> + <block name="devdocsBlock" class="Magento\Install\Test\Block\Devdocs" locator="body" strategy="css selector"/> + </page> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.php b/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.php index f7931b03e3def69ddb02f6c73c7c0381fd8cf0c1..ee51c4eee82271b2744d63167d56b82e526049a9 100644 --- a/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.php +++ b/dev/tests/functional/tests/app/Magento/Install/Test/TestCase/InstallTest.php @@ -7,6 +7,7 @@ namespace Magento\Install\Test\TestCase; use Magento\Install\Test\Page\Install; +use Magento\Install\Test\Page\DevdocsInstall; use Magento\Install\Test\Fixture\Install as InstallConfig; use Magento\User\Test\Fixture\User; use Magento\Mtf\Fixture\FixtureFactory; @@ -14,6 +15,9 @@ use Magento\Mtf\TestCase\Injectable; use Magento\Install\Test\Constraint\AssertAgreementTextPresent; use Magento\Install\Test\Constraint\AssertSuccessfulReadinessCheck; use Magento\Install\Test\Constraint\AssertAdminUriAutogenerated; +use Magento\Install\Test\Constraint\AssertDevdocsLink; +use Magento\Mtf\Util\Command\Cli\Setup; +use Magento\Mtf\Client\BrowserInterface; /** * PLEASE ADD NECESSARY INFO BEFORE RUNNING TEST TO @@ -24,24 +28,47 @@ use Magento\Install\Test\Constraint\AssertAdminUriAutogenerated; * * Steps: * 1. Go setup landing page. - * 2. Click on "Terms and agreements" button. - * 3. Check license agreement text. - * 4. Return back to landing page and click "Agree and Setup" button. - * 5. Click "Start Readiness Check" button. - * 6. Make sure PHP Version, PHP Extensions and File Permission are ok. - * 7. Click "Next" and fill DB credentials. - * 8. Click "Test Connection and Authentication" and make sure connection successful. - * 9. Click "Next" and fill store address and admin path. - * 10. Click "Next" and leave all default values. - * 11. Click "Next" and fill admin user info. - * 12. Click "Next" and on the "Step 6: Install" page click "Install Now" button. - * 13. Perform assertions. + * 2. Click on Developer Documentation link. + * 3. Check Developer Documentation title. + * 4. Click on "Terms and agreements" button. + * 5. Check license agreement text. + * 6. Return back to landing page and click "Agree and Setup" button. + * 7. Click "Start Readiness Check" button. + * 8. Make sure PHP Version, PHP Extensions and File Permission are ok. + * 9. Click "Next" and fill DB credentials. + * 10. Click "Test Connection and Authentication" and make sure connection successful. + * 11. Click "Next" and fill store address and admin path. + * 12. Click "Next" and leave all default values. + * 13. Click "Next" and fill admin user info. + * 14. Click "Next" and on the "Step 6: Install" page click "Install Now" button. + * 15. Perform assertions. * * @group Installer_and_Upgrade/Downgrade * @ZephyrId MAGETWO-31431 + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class InstallTest extends Injectable { + /** + * Developer Documentation link text. + */ + const DEVDOCS_LINK_TEXT = 'Getting Started'; + + /** + * Developer Documentation install page. + * + * @var DevdocsInstall + */ + protected $devdocsInstallPage; + + /** + * Terms and agreement selector. + * + * @var string + */ + protected $termsLink = '.text-terms>a'; + /** * Install page. * @@ -72,14 +99,16 @@ class InstallTest extends Injectable * Uninstall Magento. * * @param Install $installPage + * @param Setup $magentoSetup + * @param DevdocsInstall $devdocsInstallPage * @return void */ - public function __inject(Install $installPage) + public function __inject(Install $installPage, Setup $magentoSetup, DevdocsInstall $devdocsInstallPage) { - $magentoBaseDir = dirname(dirname(dirname(MTF_BP))); // Uninstall Magento. - shell_exec("php -f $magentoBaseDir/bin/magento setup:uninstall -n"); + $magentoSetup->uninstall(); $this->installPage = $installPage; + $this->devdocsInstallPage = $devdocsInstallPage; } /** @@ -91,6 +120,8 @@ class InstallTest extends Injectable * @param AssertAgreementTextPresent $assertLicense * @param AssertSuccessfulReadinessCheck $assertReadiness * @param AssertAdminUriAutogenerated $assertAdminUri + * @param AssertDevdocsLink $assertDevdocsLink + * @param BrowserInterface $browser * @param array $install [optional] * @return array */ @@ -101,6 +132,8 @@ class InstallTest extends Injectable AssertAgreementTextPresent $assertLicense, AssertSuccessfulReadinessCheck $assertReadiness, AssertAdminUriAutogenerated $assertAdminUri, + AssertDevdocsLink $assertDevdocsLink, + BrowserInterface $browser, array $install = [] ) { $dataConfig = array_merge($install, $configData); @@ -111,6 +144,14 @@ class InstallTest extends Injectable $installConfig = $fixtureFactory->create(\Magento\Install\Test\Fixture\Install::class, ['data' => $dataConfig]); // Steps $this->installPage->open(); + // Verify Developer Documentation link. + $handle = $browser->getCurrentWindow(); + $this->installPage->getLandingBlock()->clickLink(self::DEVDOCS_LINK_TEXT); + $this->waitTillTermsLinkNotVisible($browser); + $docHandle = $browser->getCurrentWindow(); + $assertDevdocsLink->processAssert($this->devdocsInstallPage); + $browser->closeWindow($docHandle); + $browser->selectWindow($handle); // Verify license agreement. $this->installPage->getLandingBlock()->clickTermsAndAgreement(); $assertLicense->processAssert($this->installPage); @@ -139,4 +180,20 @@ class InstallTest extends Injectable return ['installConfig' => $installConfig]; } + + /** + * Wait till terms link is not visible. + * + * @param BrowserInterface $browser + * @return void + */ + private function waitTillTermsLinkNotVisible(BrowserInterface $browser) + { + $browser->waitUntil( + function () use ($browser) { + $browser->selectWindow(); + return $browser->find($this->termsLink)->isVisible() ? null : true; + } + ); + } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create.php index ae6631eff9a24c9f323bee5bdc91b99dc355972b..b86ac18ea3dd4a70a5b3ba2530edbf89cfe1068c 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create.php @@ -279,25 +279,40 @@ class Create extends Block } /** - * Fill addresses based on present data in customer and order fixtures. + * Fill Billing Address. * - * @param FixtureInterface $address - * @param string $saveAddress + * @param FixtureInterface $billingAddress + * @param string $saveAddress [optional] * @param bool $setShippingAddress [optional] * @return void */ - public function fillAddresses(FixtureInterface $address, $saveAddress = 'No', $setShippingAddress = true) - { + public function fillBillingAddress( + FixtureInterface $billingAddress, + $saveAddress = 'No', + $setShippingAddress = true + ) { if ($setShippingAddress) { $this->getShippingAddressBlock()->uncheckSameAsBillingShippingAddress(); } - $this->browser->find($this->header)->hover(); - $this->getBillingAddressBlock()->fill($address); + $this->getBillingAddressBlock()->fill($billingAddress); $this->getBillingAddressBlock()->saveInAddressBookBillingAddress($saveAddress); $this->getTemplateBlock()->waitLoader(); - if ($setShippingAddress) { + } + + /** + * Fill Shipping Address. + * + * @param FixtureInterface $shippingAddress [optional] + * @return void + */ + public function fillShippingAddress(FixtureInterface $shippingAddress = null) + { + if (!$shippingAddress) { $this->getShippingAddressBlock()->setSameAsBillingShippingAddress(); $this->getTemplateBlock()->waitLoader(); + } else { + $this->getShippingAddressBlock()->fill($shippingAddress); + $this->getTemplateBlock()->waitLoader(); } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Address.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Address.php index 044286ea1434a369b31074b739b8ee998b405c8f..f52d898d232d145c117911d77968431001eeba70 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Address.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Address.php @@ -9,6 +9,7 @@ namespace Magento\Sales\Test\Block\Adminhtml\Order\Create\Shipping; use Magento\Mtf\Block\Form; use Magento\Mtf\Client\Locator; use Magento\Backend\Test\Block\Template; +use Magento\Mtf\Client\Element\SimpleElement; /** * Adminhtml sales order create shipping address block. @@ -29,6 +30,13 @@ class Address extends Form */ protected $sameAsBilling = '#order-shipping_same_as_billing'; + /** + * Wait element. + * + * @var string + */ + private $waitElement = '.loading-mask'; + /** * Shipping address title selector. * @@ -95,4 +103,41 @@ class Address extends Form ['element' => $this->_rootElement->find($this->templateBlock, Locator::SELECTOR_XPATH)] ); } + + /** + * Fill specified form data. + * + * @param array $fields + * @param SimpleElement $element + * @return void + * @throws \Exception + */ + protected function _fill(array $fields, SimpleElement $element = null) + { + $context = ($element === null) ? $this->_rootElement : $element; + foreach ($fields as $name => $field) { + $this->waitFormLoading(); + $element = $this->getElement($context, $field); + if (!$element->isDisabled()) { + $element->setValue($field['value']); + } else { + throw new \Exception("Unable to set value to field '$name' as it's disabled."); + } + } + } + + /** + * Wait for form loading. + * + * @return void + */ + private function waitFormLoading() + { + $this->_rootElement->click(); + $this->browser->waitUntil( + function () { + return $this->browser->find($this->waitElement)->isVisible() ? null : true; + } + ); + } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Address.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Address.xml new file mode 100644 index 0000000000000000000000000000000000000000..2dc30a686c0d9c3b997f49a2998fff971b6b1b64 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Address.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" ?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<mapping strict="0"> + <wrapper>order[shipping_address]</wrapper> + <fields> + <firstname /> + <lastname /> + <company /> + <street> + <selector>[name='order[shipping_address][street][0]']</selector> + </street> + <city /> + <country_id> + <input>select</input> + </country_id> + <region_id> + <input>select</input> + </region_id> + <postcode /> + <telephone /> + </fields> +</mapping> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Method.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Method.php index 3d9673e29805dee1383ac9b3354c4905886748e8..90a75f665cbf9f95eebe2dfa26d135082d3912f4 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Method.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Create/Shipping/Method.php @@ -28,6 +28,13 @@ class Method extends Block */ protected $shippingMethod = '//dt[contains(.,"%s")]/following-sibling::*//*[contains(text(), "%s")]'; + /** + * Wait element. + * + * @var string + */ + private $waitElement = '.loading-mask'; + /** * Select shipping method. * @@ -36,6 +43,7 @@ class Method extends Block */ public function selectShippingMethod(array $shippingMethod) { + $this->waitFormLoading(); if ($this->_rootElement->find($this->shippingMethodsLink)->isVisible()) { $this->_rootElement->find($this->shippingMethodsLink)->click(); } @@ -46,4 +54,19 @@ class Method extends Block ); $this->_rootElement->find($selector, Locator::SELECTOR_XPATH)->click(); } + + /** + * Wait for form loading. + * + * @return void + */ + private function waitFormLoading() + { + $this->_rootElement->click(); + $this->browser->waitUntil( + function () { + return $this->browser->find($this->waitElement)->isVisible() ? null : true; + } + ); + } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.php index 1779ad437c76e4192fd8c9774fc5a6f0ab8ccafb..71575ffb16b858cd43353c02d82f2d496eac4a51 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.php @@ -10,12 +10,13 @@ use Magento\Sales\Test\Fixture\OrderInjectable; use Magento\Sales\Test\Page\Adminhtml\OrderIndex; use Magento\Sales\Test\Page\Adminhtml\SalesOrderView; use Magento\Mtf\TestCase\Injectable; +use Magento\Mtf\TestStep\TestStepFactory; /** * Preconditions: - * 1. Enable payment method "Check/Money Order". - * 2. Enable shipping method one of "Flat Rate". - * 3. Create order + * 1. Enable payment method: "Check/Money Order/Bank Transfer/Cash on Delivery/Purchase Order/Zero Subtotal Checkout". + * 2. Enable shipping method one of "Flat Rate/Free Shipping". + * 3. Create order. * * Steps: * 1. Login to backend. @@ -48,20 +49,7 @@ class CancelCreatedOrderTest extends Injectable protected $salesOrderView; /** - * Enable "Check/Money Order" and "Flat Rate" in configuration. - * - * @return void - */ - public function __prepare() - { - $this->objectManager->create( - \Magento\Config\Test\TestStep\SetupConfigurationStep::class, - ['configData' => 'checkmo, flatrate', 'rollback' => true] - )->run(); - } - - /** - * Inject pages + * Inject pages. * * @param OrderIndex $orderIndex * @param SalesOrderView $salesOrderView @@ -77,11 +65,17 @@ class CancelCreatedOrderTest extends Injectable * Cancel created order. * * @param OrderInjectable $order + * @param TestStepFactory $stepFactory + * @param string $configData * @return array */ - public function test(OrderInjectable $order) + public function test(OrderInjectable $order, TestStepFactory $stepFactory, $configData) { // Preconditions + $stepFactory->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $configData] + )->run(); $order->persist(); // Steps diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml index bcacacc57589a88806a2bcad18912ab84760ec8f..b65b9a31f72f3d7ff7290afb4393edeaf73679bc 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml @@ -7,14 +7,49 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Sales\Test\TestCase\CancelCreatedOrderTest" summary="Cancel Created Order for Offline Payment Methods" ticketId="MAGETWO-28191"> - <variation name="CancelCreatedOrderTestVariation1"> - <data name="description" xsi:type="string">cancel order and check status on storefront</data> + <variation name="CancelCreatedOrderTestVariationWithCheckMoneyOrderPaymentMethod" summary="Cancel order with check/money order payment method and check status on storefront"> <data name="order/dataset" xsi:type="string">default</data> <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::default,catalogProductSimple::default</data> <data name="status" xsi:type="string">Canceled</data> + <data name="configData" xsi:type="string">checkmo</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelSuccessMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGrid" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGridOnFrontend" /> </variation> + <variation name="CancelCreatedOrderTestVariationWithZeroSubtotalCheckout" summary="Cancel order with zero subtotal checkout payment method and check status on storefront"> + <data name="order/dataset" xsi:type="string">default</data> + <data name="order/data/payment_auth_expiration/method" xsi:type="string">free</data> + <data name="order/data/shipping_method" xsi:type="string">freeshipping_freeshipping</data> + <data name="order/data/coupon_code/dataset" xsi:type="string">active_sales_rule_with_fixed_price_discount_coupon</data> + <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::product_10_dollar</data> + <data name="status" xsi:type="string">Canceled</data> + <data name="configData" xsi:type="string">zero_subtotal_checkout, freeshipping</data> + <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelSuccessMessage" /> + <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGridOnFrontend" /> + </variation> + <variation name="CancelCreatedOrderTestVariationWithBankTransferPaymentMethod" summary="Cancel order with bank transfer payment method and check status on storefront"> + <data name="order/dataset" xsi:type="string">default</data> + <data name="order/data/payment_auth_expiration/method" xsi:type="string">banktransfer</data> + <data name="status" xsi:type="string">Canceled</data> + <data name="configData" xsi:type="string">banktransfer</data> + <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelSuccessMessage" /> + <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGridOnFrontend" /> + </variation> + <variation name="CancelCreatedOrderTestVariationWithCashOnDeliveryPaymentMethod" summary="Cancel order with cash on delivery payment method and check status on storefront"> + <data name="order/dataset" xsi:type="string">default</data> + <data name="order/data/payment_auth_expiration/method" xsi:type="string">cashondelivery</data> + <data name="status" xsi:type="string">Canceled</data> + <data name="configData" xsi:type="string">cashondelivery</data> + <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelSuccessMessage" /> + <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGridOnFrontend" /> + </variation> + <variation name="CancelCreatedOrderTestVariationWithPurchaseOrderPaymentMethod" summary="Cancel order with purchase order payment method and check status on storefront"> + <data name="order/dataset" xsi:type="string">default</data> + <data name="order/data/payment_auth_expiration/method" xsi:type="string">purchaseorder</data> + <data name="status" xsi:type="string">Canceled</data> + <data name="configData" xsi:type="string">purchaseorder</data> + <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelSuccessMessage" /> + <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGridOnFrontend" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCreditMemoEntityTest.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCreditMemoEntityTest.php index ed00a414bd0e689cfdb15b75ccf5d795ca40c992..9d19d10f4d40c6c3e317a97e7e83cd3c79e6f8ae 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCreditMemoEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCreditMemoEntityTest.php @@ -10,10 +10,11 @@ use Magento\Sales\Test\Fixture\OrderInjectable; use Magento\Mtf\Fixture\FixtureFactory; use Magento\Mtf\Fixture\FixtureInterface; use Magento\Mtf\TestCase\Injectable; +use Magento\Mtf\TestStep\TestStepFactory; /** * Preconditions: - * 1. Enable payment method "Check/Money Order". + * 1. Enable payment method one of "Check/Money Order/Bank Transfer/Cash on Delivery/Purchase Order". * 2. Enable shipping method one of "Flat Rate/Free Shipping". * 3. Create order. * 4. Create Invoice. @@ -54,38 +55,34 @@ class CreateCreditMemoEntityTest extends Injectable 'price', ]; - /** - * Set up configuration. - * - * @param FixtureFactory $fixtureFactory - * @return void - */ - public function __prepare(FixtureFactory $fixtureFactory) - { - $this->fixtureFactory = $fixtureFactory; - - $setupConfigurationStep = $this->objectManager->create( - \Magento\Config\Test\TestStep\SetupConfigurationStep::class, - ['configData' => 'checkmo, flatrate'] - ); - $setupConfigurationStep->run(); - } - /** * Create credit memo. * + * @param TestStepFactory $stepFactory + * @param FixtureFactory $fixtureFactory * @param OrderInjectable $order * @param array $data + * @param string $configData * @return array */ - public function test(OrderInjectable $order, array $data) - { + public function test( + TestStepFactory $stepFactory, + FixtureFactory $fixtureFactory, + OrderInjectable $order, + array $data, + $configData + ) { // Preconditions + $this->fixtureFactory = $fixtureFactory; + $stepFactory->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $configData] + )->run(); $order->persist(); - $this->objectManager->create(\Magento\Sales\Test\TestStep\CreateInvoiceStep::class, ['order' => $order])->run(); + $stepFactory->create(\Magento\Sales\Test\TestStep\CreateInvoiceStep::class, ['order' => $order])->run(); // Steps - $createCreditMemoStep = $this->objectManager->create( + $createCreditMemoStep = $stepFactory->create( \Magento\Sales\Test\TestStep\CreateCreditMemoStep::class, ['order' => $order, 'data' => $data] ); diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCreditMemoEntityTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCreditMemoEntityTest.xml index 80e1bfdf81e8f39b5547fd96def138063fa00231..02b9640acbea4159c0560e8a4727a13cc36e1199 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCreditMemoEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateCreditMemoEntityTest.xml @@ -7,14 +7,14 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Sales\Test\TestCase\CreateCreditMemoEntityTest" summary="Create Credit Memo for Offline Payment Methods" ticketId="MAGETWO-29116"> - <variation name="CreateCreditMemoEntityTestVariation1"> - <data name="description" xsi:type="string">Assert items return to stock (partial refund)</data> + <variation name="CreateCreditMemoEntityTestVariation1" summary="Assert items return to stock (partial refund)"> <data name="data/items_data/0/back_to_stock" xsi:type="string">Yes</data> <data name="data/items_data/0/qty" xsi:type="string">1</data> <data name="data/form_data/send_email" xsi:type="string">Yes</data> <data name="order/dataset" xsi:type="string">default</data> <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::product_100_dollar</data> <data name="order/data/price/dataset" xsi:type="string">partial_refund</data> + <data name="configData" xsi:type="string">checkmo</data> <constraint name="Magento\Sales\Test\Constraint\AssertRefundSuccessCreateMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertCreditMemoButton" /> <constraint name="Magento\Sales\Test\Constraint\AssertRefundInCreditMemoTab" /> @@ -25,15 +25,15 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductForm" /> <constraint name="Magento\Sales\Test\Constraint\AssertCreditMemoItems" /> </variation> - <variation name="CreateCreditMemoEntityTestVariation2"> - <data name="description" xsi:type="string">Assert 0 shipping refund</data> + <variation name="CreateCreditMemoEntityTestVariation2" summary="Assert 0 shipping refund"> <data name="data/items_data/0/qty" xsi:type="string">1</data> <data name="data/form_data/shipping_amount" xsi:type="string">0</data> <data name="data/form_data/adjustment_positive" xsi:type="string">5</data> <data name="data/form_data/adjustment_negative" xsi:type="string">10</data> <data name="order/dataset" xsi:type="string">default</data> - <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::default</data> + <data name="order/data/payment_auth_expiration/method" xsi:type="string">banktransfer</data> <data name="order/data/price/dataset" xsi:type="string">full_refund_with_zero_shipping_refund</data> + <data name="configData" xsi:type="string">banktransfer</data> <constraint name="Magento\Sales\Test\Constraint\AssertRefundSuccessCreateMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertCreditMemoButton" /> <constraint name="Magento\Sales\Test\Constraint\AssertRefundInCreditMemoTab" /> @@ -41,5 +41,29 @@ <constraint name="Magento\Sales\Test\Constraint\AssertRefundedGrandTotalOnFrontend" /> <constraint name="Magento\Sales\Test\Constraint\AssertCreditMemoItems" /> </variation> + <variation name="CreateCreditMemoEntityTestVariationWithCashOnDeliveryPaymentMethod" summary="Assert 0 shipping refund with Cash on delivery payment method"> + <data name="data/items_data/0/qty" xsi:type="string">1</data> + <data name="data/form_data/shipping_amount" xsi:type="string">0</data> + <data name="data/form_data/adjustment_positive" xsi:type="string">5</data> + <data name="data/form_data/adjustment_negative" xsi:type="string">10</data> + <data name="order/dataset" xsi:type="string">default</data> + <data name="order/data/payment_auth_expiration/method" xsi:type="string">cashondelivery</data> + <data name="order/data/price/dataset" xsi:type="string">full_refund_with_zero_shipping_refund</data> + <data name="configData" xsi:type="string">cashondelivery</data> + <constraint name="Magento\Sales\Test\Constraint\AssertRefundSuccessCreateMessage" /> + <constraint name="Magento\Sales\Test\Constraint\AssertRefundedGrandTotalOnFrontend" /> + </variation> + <variation name="CreateCreditMemoEntityTestVariationWithPurchaseOrderPaymentMethod" summary="Assert 0 shipping refund with Purchase Order payment method"> + <data name="data/items_data/0/qty" xsi:type="string">1</data> + <data name="data/form_data/shipping_amount" xsi:type="string">0</data> + <data name="data/form_data/adjustment_positive" xsi:type="string">5</data> + <data name="data/form_data/adjustment_negative" xsi:type="string">10</data> + <data name="order/dataset" xsi:type="string">default</data> + <data name="order/data/payment_auth_expiration/method" xsi:type="string">purchaseorder</data> + <data name="order/data/price/dataset" xsi:type="string">full_refund_with_zero_shipping_refund</data> + <data name="configData" xsi:type="string">purchaseorder</data> + <constraint name="Magento\Sales\Test\Constraint\AssertRefundSuccessCreateMessage" /> + <constraint name="Magento\Sales\Test\Constraint\AssertRefundedGrandTotalOnFrontend" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateInvoiceEntityTest.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateInvoiceEntityTest.php index f62583583c3dc565579196d24ed564a14613eef9..f7cdaf74ab0f57e0544a392f6a936498bcb8efee 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateInvoiceEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateInvoiceEntityTest.php @@ -8,10 +8,11 @@ namespace Magento\Sales\Test\TestCase; use Magento\Sales\Test\Fixture\OrderInjectable; use Magento\Mtf\TestCase\Injectable; +use Magento\Mtf\TestStep\TestStepFactory; /** * Preconditions: - * 1. Enable payment method "Check/Money Order". + * 1. Enable payment method: "Check/Money Order/Bank Transfer/Cash on Delivery/Purchase Order/Zero Subtotal Checkout". * 2. Enable shipping method one of "Flat Rate/Free Shipping". * 3. Create order. * @@ -33,16 +34,21 @@ class CreateInvoiceEntityTest extends Injectable /* end tags */ /** - * Set up configuration. + * Factory for Test Steps. * + * @var TestStepFactory + */ + protected $stepFactory; + + /** + * Prepare data. + * + * @param TestStepFactory $stepFactory * @return void */ - public function __prepare() + public function __prepare(TestStepFactory $stepFactory) { - $this->objectManager->create( - \Magento\Config\Test\TestStep\SetupConfigurationStep::class, - ['configData' => 'checkmo, flatrate'] - )->run(); + $this->stepFactory = $stepFactory; } /** @@ -50,15 +56,20 @@ class CreateInvoiceEntityTest extends Injectable * * @param OrderInjectable $order * @param array $data + * @param string $configData * @return array */ - public function test(OrderInjectable $order, array $data) + public function test(OrderInjectable $order, array $data, $configData) { // Preconditions + $this->stepFactory->create( + \Magento\Config\Test\TestStep\SetupConfigurationStep::class, + ['configData' => $configData] + )->run(); $order->persist(); // Steps - $result = $this->objectManager->create( + $result = $this->stepFactory->create( \Magento\Sales\Test\TestStep\CreateInvoiceStep::class, ['order' => $order, 'data' => $data] )->run(); @@ -73,6 +84,6 @@ class CreateInvoiceEntityTest extends Injectable */ public function tearDown() { - $this->objectManager->create(\Magento\Customer\Test\TestStep\LogoutCustomerOnFrontendStep::class)->run(); + $this->stepFactory->create(\Magento\Customer\Test\TestStep\LogoutCustomerOnFrontendStep::class)->run(); } } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateInvoiceEntityTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateInvoiceEntityTest.xml index afc4bad5e706db538cd5957201a4a5c9347a3605..8753de91d1f4301710f15a968b7a7ea24e04d663 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateInvoiceEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateInvoiceEntityTest.xml @@ -10,11 +10,11 @@ <variation name="CreateInvoiceEntityTestVariation1"> <data name="order/dataset" xsi:type="string">default</data> <data name="order/data/price/dataset" xsi:type="string">full_invoice</data> - <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::default</data> <data name="order/data/total_qty_ordered/0" xsi:type="string">1</data> <data name="data/items_data/0/qty" xsi:type="string">-</data> <data name="data/form_data/do_shipment" xsi:type="string">Yes</data> <data name="data/form_data/comment_text" xsi:type="string">comments</data> + <data name="configData" xsi:type="string">checkmo</data> <constraint name="Magento\Sales\Test\Constraint\AssertInvoiceWithShipmentSuccessMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertNoInvoiceButton" /> <constraint name="Magento\Sales\Test\Constraint\AssertInvoiceInInvoicesTab" /> @@ -29,14 +29,57 @@ <data name="order/data/price/dataset" xsi:type="string">partial_invoice</data> <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::product_100_dollar</data> <data name="order/data/total_qty_ordered/0" xsi:type="string">-</data> + <data name="order/data/payment_auth_expiration/method" xsi:type="string">banktransfer</data> <data name="data/items_data/0/qty" xsi:type="string">1</data> <data name="data/form_data/do_shipment" xsi:type="string">No</data> <data name="data/form_data/comment_text" xsi:type="string">comments</data> + <data name="configData" xsi:type="string">banktransfer</data> <constraint name="Magento\Sales\Test\Constraint\AssertInvoiceSuccessCreateMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertInvoiceInInvoicesTab" /> <constraint name="Magento\Sales\Test\Constraint\AssertInvoiceInInvoicesGrid" /> <constraint name="Magento\Sales\Test\Constraint\AssertInvoiceItems" /> <constraint name="Magento\Sales\Test\Constraint\AssertInvoicedAmountOnFrontend" /> </variation> + <variation name="CreateInvoiceEntityTestVariationWithCashOnDeliveryPaymentMethod"> + <data name="order/dataset" xsi:type="string">default</data> + <data name="order/data/price/dataset" xsi:type="string">partial_invoice</data> + <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::product_100_dollar</data> + <data name="order/data/total_qty_ordered/0" xsi:type="string">-</data> + <data name="order/data/payment_auth_expiration/method" xsi:type="string">cashondelivery</data> + <data name="data/items_data/0/qty" xsi:type="string">1</data> + <data name="data/form_data/do_shipment" xsi:type="string">No</data> + <data name="data/form_data/comment_text" xsi:type="string">comments</data> + <data name="configData" xsi:type="string">cashondelivery</data> + <constraint name="Magento\Sales\Test\Constraint\AssertInvoiceSuccessCreateMessage" /> + <constraint name="Magento\Sales\Test\Constraint\AssertInvoicedAmountOnFrontend" /> + </variation> + <variation name="CreateInvoiceEntityTestVariationWithPurchaseOrderPaymentMethod"> + <data name="order/dataset" xsi:type="string">default</data> + <data name="order/data/price/dataset" xsi:type="string">partial_invoice</data> + <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::product_100_dollar</data> + <data name="order/data/total_qty_ordered/0" xsi:type="string">-</data> + <data name="order/data/payment_auth_expiration/method" xsi:type="string">purchaseorder</data> + <data name="data/items_data/0/qty" xsi:type="string">1</data> + <data name="data/form_data/do_shipment" xsi:type="string">No</data> + <data name="data/form_data/comment_text" xsi:type="string">comments</data> + <data name="configData" xsi:type="string">purchaseorder</data> + <constraint name="Magento\Sales\Test\Constraint\AssertInvoiceSuccessCreateMessage" /> + <constraint name="Magento\Sales\Test\Constraint\AssertInvoicedAmountOnFrontend" /> + </variation> + <variation name="CreateInvoiceEntityTestVariationWithZeroSubtotalCheckout"> + <data name="order/dataset" xsi:type="string">default</data> + <data name="order/data/price/dataset" xsi:type="string">partial_invoice</data> + <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::product_10_dollar</data> + <data name="order/data/total_qty_ordered/0" xsi:type="string">-</data> + <data name="order/data/payment_auth_expiration/method" xsi:type="string">free</data> + <data name="order/data/shipping_method" xsi:type="string">freeshipping_freeshipping</data> + <data name="order/data/coupon_code/dataset" xsi:type="string">active_sales_rule_with_fixed_price_discount_coupon</data> + <data name="data/items_data/0/qty" xsi:type="string">1</data> + <data name="data/form_data/do_shipment" xsi:type="string">No</data> + <data name="data/form_data/comment_text" xsi:type="string">comments</data> + <data name="configData" xsi:type="string">zero_subtotal_checkout, freeshipping</data> + <constraint name="Magento\Sales\Test\Constraint\AssertInvoiceSuccessCreateMessage" /> + <constraint name="Magento\Sales\Test\Constraint\AssertInvoicedAmountOnFrontend" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/FillBillingAddressStep.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/FillBillingAddressStep.php index bde5bff6de9d4e1f53969137ac86c94d8323c7fd..f6d0c8325bfc38500c4cd5a5d7289e8b3416439a 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/FillBillingAddressStep.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/FillBillingAddressStep.php @@ -11,7 +11,7 @@ use Magento\Sales\Test\Page\Adminhtml\OrderCreateIndex; use Magento\Mtf\TestStep\TestStepInterface; /** - * Fill Sales Data. + * Fill Billing Address. */ class FillBillingAddressStep implements TestStepInterface { @@ -23,7 +23,7 @@ class FillBillingAddressStep implements TestStepInterface protected $orderCreateIndex; /** - * Address. + * Billing Address fixture. * * @var Address */ @@ -37,14 +37,13 @@ class FillBillingAddressStep implements TestStepInterface protected $saveAddress; /** - * Flag for set same as billing shipping address. + * Flag to set 'Same as billing address' for shipping address. * * @var string */ protected $setShippingAddress; /** - * @constructor * @param OrderCreateIndex $orderCreateIndex * @param Address $billingAddress * @param string $saveAddress @@ -63,14 +62,14 @@ class FillBillingAddressStep implements TestStepInterface } /** - * Fill Sales Data. + * Fill Billing Address. * - * @return Address + * @return array */ public function run() { $this->orderCreateIndex->getCreateBlock() - ->fillAddresses($this->billingAddress, $this->saveAddress, $this->setShippingAddress); + ->fillBillingAddress($this->billingAddress, $this->saveAddress, $this->setShippingAddress); return ['billingAddress' => $this->billingAddress]; } diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/FillShippingAddressStep.php b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/FillShippingAddressStep.php new file mode 100644 index 0000000000000000000000000000000000000000..8ff05dfca95bb016b4b72e5db07f5fa2ffb139ca --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestStep/FillShippingAddressStep.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Sales\Test\TestStep; + +use Magento\Customer\Test\Fixture\Address; +use Magento\Sales\Test\Page\Adminhtml\OrderCreateIndex; +use Magento\Mtf\TestStep\TestStepInterface; + +/** + * Fill Shipping Address. + */ +class FillShippingAddressStep implements TestStepInterface +{ + /** + * Sales order create index page. + * + * @var OrderCreateIndex + */ + protected $orderCreateIndex; + + /** + * Shipping Address fixture. + * + * @var Address + */ + protected $shippingAddress; + + /** + * @param OrderCreateIndex $orderCreateIndex + * @param Address $shippingAddress [optional] + */ + public function __construct( + OrderCreateIndex $orderCreateIndex, + Address $shippingAddress = null + ) { + $this->orderCreateIndex = $orderCreateIndex; + $this->shippingAddress = $shippingAddress; + } + + /** + * Fill Shipping Address. + * + * @return void + */ + public function run() + { + $this->orderCreateIndex->getCreateBlock()->fillShippingAddress($this->shippingAddress); + } +} diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/etc/testcase.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/etc/testcase.xml index c48430b00e7ea4412265dfaf4d91ed85cbd2819c..ec2da3f2607b2dda7e4b375e11192f9844d839fd 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/etc/testcase.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/etc/testcase.xml @@ -28,7 +28,8 @@ <step name="addProducts" module="Magento_Sales" next="updateProductsData" /> <step name="updateProductsData" module="Magento_Sales" next="fillAccountInformation" /> <step name="fillAccountInformation" module="Magento_Sales" next="fillBillingAddress" /> - <step name="fillBillingAddress" module="Magento_Sales" next="selectShippingMethodForOrder" /> + <step name="fillBillingAddress" module="Magento_Sales" next="fillShippingAddress" /> + <step name="fillShippingAddress" module="Magento_Sales" next="selectShippingMethodForOrder" /> <step name="selectShippingMethodForOrder" module="Magento_Sales" next="selectPaymentMethodForOrder" /> <step name="selectPaymentMethodForOrder" module="Magento_Sales" next="submitOrder" /> <step name="submitOrder" module="Magento_Sales" /> diff --git a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/AbstractFormContainers.php b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/AbstractFormContainers.php index b6ed37af715be90fc600aa5728744ccd3a160d68..180afa8fe7ec6b3a862ef9f80e077fcca70b354e 100644 --- a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/AbstractFormContainers.php +++ b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/AbstractFormContainers.php @@ -158,7 +158,7 @@ abstract class AbstractFormContainers extends Form /** * Fill specified form with containers data. - * + * * Input data in format * [[container => [field => [attribute_name => attribute_value, ..], ..], ..] * where container name can be empty if a field is not assigned to any container. diff --git a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php index 3fc6f044f88521794f579aa653bacd6d87e44d14..80df7e6f8c631722fb314525b2d6733e2ac605ad 100644 --- a/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php +++ b/dev/tests/functional/tests/app/Magento/Ui/Test/Block/Adminhtml/DataGrid.php @@ -142,7 +142,7 @@ class DataGrid extends Grid * * @var string */ - protected $currentPage = '[data-ui-id="current-page-input"]'; + protected $currentPage = ".//*[@data-ui-id='current-page-input'][not(ancestor::*[@class='sticky-header'])]"; /** * Clear all applied Filters. @@ -347,7 +347,7 @@ class DataGrid extends Grid $this->sortGridByField('ID'); } foreach ($items as $item) { - $this->_rootElement->find($this->currentPage)->setValue(''); + $this->_rootElement->find($this->currentPage, Locator::SELECTOR_XPATH)->setValue(''); $this->waitLoader(); $selectItem = $this->getRow($item)->find($this->selectItem); do { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/CreateHandlerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/CreateHandlerTest.php index 992c3bf310f51995ece1b78f6c566fd1d3baf1a4..2c42a289bd4c017a15591d9a110220fce74de346 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/CreateHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/CreateHandlerTest.php @@ -18,6 +18,10 @@ class CreateHandlerTest extends \PHPUnit_Framework_TestCase */ protected $createHandler; + private $fileName = '/m/a/magento_image.jpg'; + + private $fileLabel = 'Magento image'; + protected function setUp() { $this->createHandler = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( @@ -28,10 +32,8 @@ class CreateHandlerTest extends \PHPUnit_Framework_TestCase /** * @covers \Magento\Catalog\Model\Product\Gallery\CreateHandler::execute */ - public function testExecute() + public function testExecuteWithImageDuplicate() { - $fileName = '/m/a/magento_image.jpg'; - $fileLabel = 'Magento image'; /** @var $product \Magento\Catalog\Model\Product */ $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\Catalog\Model\Product::class @@ -39,20 +41,141 @@ class CreateHandlerTest extends \PHPUnit_Framework_TestCase $product->load(1); $product->setData( 'media_gallery', - ['images' => ['image' => ['file' => $fileName, 'label' => $fileLabel]]] + ['images' => ['image' => ['file' => $this->fileName, 'label' => $this->fileLabel]]] ); - $product->setData('image', $fileName); + $product->setData('image', $this->fileName); $this->createHandler->execute($product); $this->assertStringStartsWith('/m/a/magento_image', $product->getData('media_gallery/images/image/new_file')); - $this->assertEquals($fileLabel, $product->getData('image_label')); + $this->assertEquals($this->fileLabel, $product->getData('image_label')); $product->setIsDuplicate(true); $product->setData( 'media_gallery', - ['images' => ['image' => ['value_id' => '100', 'file' => $fileName, 'label' => $fileLabel]]] + ['images' => ['image' => ['value_id' => '100', 'file' => $this->fileName, 'label' => $this->fileLabel]]] ); $this->createHandler->execute($product); $this->assertStringStartsWith('/m/a/magento_image', $product->getData('media_gallery/duplicate/100')); - $this->assertEquals($fileLabel, $product->getData('image_label')); + $this->assertEquals($this->fileLabel, $product->getData('image_label')); + } + + /** + * @dataProvider executeDataProvider + * @param $image + * @param $smallImage + * @param $swatchImage + * @param $thumbnail + */ + public function testExecuteWithImageRoles($image, $smallImage, $swatchImage, $thumbnail) + { + /** @var $product \Magento\Catalog\Model\Product */ + $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Model\Product::class + ); + $product->load(1); + $product->setData( + 'media_gallery', + ['images' => ['image' => ['file' => $this->fileName, 'label' => '']]] + ); + $product->setData('image', $image); + $product->setData('small_image', $smallImage); + $product->setData('swatch_image', $swatchImage); + $product->setData('thumbnail', $thumbnail); + $this->createHandler->execute($product); + + $resource = $product->getResource(); + $id = $product->getId(); + $storeId = $product->getStoreId(); + + $this->assertStringStartsWith('/m/a/magento_image', $product->getData('media_gallery/images/image/new_file')); + $this->assertEquals( + $image, + $resource->getAttributeRawValue($id, $resource->getAttribute('image'), $storeId) + ); + $this->assertEquals( + $smallImage, + $resource->getAttributeRawValue($id, $resource->getAttribute('small_image'), $storeId) + ); + $this->assertEquals( + $swatchImage, + $resource->getAttributeRawValue($id, $resource->getAttribute('swatch_image'), $storeId) + ); + $this->assertEquals( + $thumbnail, + $resource->getAttributeRawValue($id, $resource->getAttribute('thumbnail'), $storeId) + ); + } + + /** + * @dataProvider executeDataProvider + * @param $image + * @param $smallImage + * @param $swatchImage + * @param $thumbnail + */ + public function testExecuteWithoutImages($image, $smallImage, $swatchImage, $thumbnail) + { + /** @var $product \Magento\Catalog\Model\Product */ + $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Model\Product::class + ); + $product->load(1); + $product->setData( + 'media_gallery', + ['images' => ['image' => ['file' => $this->fileName, 'label' => '']]] + ); + $product->setData('image', $image); + $product->setData('small_image', $smallImage); + $product->setData('swatch_image', $swatchImage); + $product->setData('thumbnail', $thumbnail); + $this->createHandler->execute($product); + + $product->unsetData('image'); + $product->unsetData('small_image'); + $product->unsetData('swatch_image'); + $product->unsetData('thumbnail'); + $this->createHandler->execute($product); + + $resource = $product->getResource(); + $id = $product->getId(); + $storeId = $product->getStoreId(); + + $this->assertStringStartsWith('/m/a/magento_image', $product->getData('media_gallery/images/image/new_file')); + $this->assertEquals( + $image, + $resource->getAttributeRawValue($id, $resource->getAttribute('image'), $storeId) + ); + $this->assertEquals( + $smallImage, + $resource->getAttributeRawValue($id, $resource->getAttribute('small_image'), $storeId) + ); + $this->assertEquals( + $swatchImage, + $resource->getAttributeRawValue($id, $resource->getAttribute('swatch_image'), $storeId) + ); + $this->assertEquals( + $thumbnail, + $resource->getAttributeRawValue($id, $resource->getAttribute('thumbnail'), $storeId) + ); + } + + /** + * @return array + */ + public function executeDataProvider() + { + return [ + [ + 'image' => $this->fileName, + 'small_image' => $this->fileName, + 'swatch_image' => $this->fileName, + 'thumbnail' => $this->fileName + ], + [ + 'image' => 'no_selection', + 'small_image' => 'no_selection', + 'swatch_image' => 'no_selection', + 'thumbnail' => 'no_selection' + ] + ]; } } 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 aadf1e74a883ce823d574178cba29ca617f5abf0..3125cc2a690ba9a0788079f37be339e5e6b5c28f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php @@ -160,14 +160,14 @@ $oldOptions = [ 'sort_order' => 0, 'values' => [ [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 1', 'price' => 3, 'price_type' => 'fixed', 'sku' => '3-1-select', ], [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 2', 'price' => 3, 'price_type' => 'fixed', @@ -183,14 +183,14 @@ $oldOptions = [ 'sort_order' => 0, 'values' => [ [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 1', 'price' => 3, 'price_type' => 'fixed', 'sku' => '4-1-radio', ], [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 2', 'price' => 3, 'price_type' => 'fixed', diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_admin_store.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_admin_store.php index 18c70968039f8f6df0056daa98da1dbef068e055..d933cd00656d3c8ffde5bd37526aa31aaacba681 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_admin_store.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_admin_store.php @@ -98,14 +98,14 @@ $oldOptions = [ 'sort_order' => 0, 'values' => [ [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 1', 'price' => 3, 'price_type' => 'fixed', 'sku' => '3-1-select', ], [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 2', 'price' => 3, 'price_type' => 'fixed', @@ -121,14 +121,14 @@ $oldOptions = [ 'sort_order' => 0, 'values' => [ [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 1', 'price' => 3, 'price_type' => 'fixed', 'sku' => '4-1-radio', ], [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 2', 'price' => 3, 'price_type' => 'fixed', diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_options.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_options.php index 0366e90cd9772036315678f6e3b10d1f59b615fb..a0a29753bfeed384aa1a4131e4ca2fed26491e95 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_options.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_custom_options.php @@ -49,14 +49,14 @@ $oldOptions = [ 'sort_order' => 0, 'values' => [ [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 1', 'price' => '3,000.00', 'price_type' => 'fixed', 'sku' => '3-1-select', ], [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 2', 'price' => '5,000.00', 'price_type' => 'fixed', @@ -72,14 +72,14 @@ $oldOptions = [ 'sort_order' => 0, 'values' => [ [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 1', 'price' => '600.234', 'price_type' => 'fixed', 'sku' => '4-1-radio', ], [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 2', 'price' => '40,000.00', 'price_type' => 'fixed', 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 9f3d16a998987d600e1f702f4a4228e1ff06c9b7..43ac499b8bd9dfce4312896c97ab506a4c93f02d 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -20,6 +20,7 @@ use Magento\Catalog\Model\Category; use Magento\Framework\App\Bootstrap; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\ImportExport\Model\Import; +use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface; /** * Class ProductTest @@ -1050,9 +1051,9 @@ class ProductTest extends \Magento\TestFramework\Indexer\TestCase * @magentoAppIsolation enabled * @dataProvider validateUrlKeysDataProvider * @param $importFile string - * @param $errorsCount int + * @param $expectedErrors array */ - public function testValidateUrlKeys($importFile, $errorsCount) + public function testValidateUrlKeys($importFile, $expectedErrors) { $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\Framework\Filesystem::class @@ -1066,19 +1067,16 @@ class ProductTest extends \Magento\TestFramework\Indexer\TestCase 'directory' => $directory ] ); + /** @var \Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface $errors */ $errors = $this->_model->setParameters( ['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, 'entity' => 'catalog_product'] )->setSource( $source )->validateData(); - - $this->assertTrue($errors->getErrorsCount() == $errorsCount); - if ($errorsCount >= 1) { - $this->assertEquals( - "Specified URL key already exists", - $errors->getErrorByRowNumber(1)[0]->getErrorMessage() - ); - } + $this->assertEquals( + $expectedErrors[RowValidatorInterface::ERROR_DUPLICATE_URL_KEY], + count($errors->getErrorsByCode([RowValidatorInterface::ERROR_DUPLICATE_URL_KEY])) + ); } /** @@ -1087,9 +1085,24 @@ class ProductTest extends \Magento\TestFramework\Indexer\TestCase public function validateUrlKeysDataProvider() { return [ - ['products_to_check_valid_url_keys.csv', 0], - ['products_to_check_duplicated_url_keys.csv', 2], - ['products_to_check_duplicated_names.csv' , 1] + [ + 'products_to_check_valid_url_keys.csv', + [ + RowValidatorInterface::ERROR_DUPLICATE_URL_KEY => 0 + ] + ], + [ + 'products_to_check_duplicated_url_keys.csv', + [ + RowValidatorInterface::ERROR_DUPLICATE_URL_KEY => 2 + ] + ], + [ + 'products_to_check_duplicated_names.csv' , + [ + RowValidatorInterface::ERROR_DUPLICATE_URL_KEY => 1 + ] + ] ]; } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php index a3bf1686b5fdb2181f28939a1bc10d36170532ea..ffee568b0622fa8ef3f5af185e24805367ee9203 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php @@ -18,16 +18,30 @@ $productModel = $objectManager->create(\Magento\Catalog\Model\Product::class); $customOptions = [ [ - 'id' => 'test_option_code_1', - 'option_id' => '0', + 'option_id' => null, 'sort_order' => '0', 'title' => 'Option 1', 'type' => 'drop_down', 'is_require' => 1, 'values' => [ - 1 => ['option_type_id' => -1, 'title' => 'Option 1 & Value 1"', 'price' => '1.00', 'price_type' => 'fixed'], - 2 => ['option_type_id' => -1, 'title' => 'Option 1 & Value 2"', 'price' => '2.00', 'price_type' => 'fixed'], - 3 => ['option_type_id' => -1, 'title' => 'Option 1 & Value 3"', 'price' => '3.00', 'price_type' => 'fixed'] + 1 => [ + 'option_type_id' => null, + 'title' => 'Option 1 & Value 1"', + 'price' => '1.00', + 'price_type' => 'fixed' + ], + 2 => [ + 'option_type_id' => null, + 'title' => 'Option 1 & Value 2"', + 'price' => '2.00', + 'price_type' => 'fixed' + ], + 3 => [ + 'option_type_id' => null, + 'title' => 'Option 1 & Value 3"', + 'price' => '3.00', + 'price_type' => 'fixed' + ] ] ], [ diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/PageTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/PageTest.php index 65e578c1181db4cc44d463d62bf210d4a26a55ee..525b3161ffcd921b6b0b9ea048b1f8e077c094f8 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/Model/PageTest.php +++ b/dev/tests/integration/testsuite/Magento/Cms/Model/PageTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Cms\Model; +use Magento\Cms\Api\PageRepositoryInterface; + /** * @magentoAppArea adminhtml */ @@ -44,6 +46,22 @@ class PageTest extends \PHPUnit_Framework_TestCase $this->assertEquals($expectedIdentifier, $page->getIdentifier()); } + /** + * @magentoDbIsolation enabled + */ + public function testUpdateTime() + { + $updateTime = '2016-09-01 00:00:00'; + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var \Magento\Cms\Model\Page $page */ + $page = $objectManager->create(\Magento\Cms\Model\Page::class); + $page->setData(['title' => 'Test', 'stores' => [1]]); + $page->setUpdateTime($updateTime); + $page->save(); + $page = $objectManager->get(PageRepositoryInterface::class)->getById($page->getId()); + $this->assertEquals($updateTime, $page->getUpdateTime()); + } + public function generateIdentifierFromTitleDataProvider() { return [ diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php index 053e33da09fe9f812aa7a1d4164cf274467a3a6c..6322a60cef8a5fda1b50e220b10de7c5385956d4 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php @@ -90,8 +90,7 @@ class PriceTest extends \PHPUnit_Framework_TestCase $options = $this->prepareOptions( [ [ - 'id' => 1, - 'option_id' => 0, + 'option_id' => null, 'previous_group' => 'text', 'title' => 'Test Field', 'type' => 'field', diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_simple_77.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_simple_77.php index b35d87d48063a39011a92e5184090402ea64b4c0..ff08056e4b18def3f520d2a5d9f2090b168d5ec8 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_simple_77.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_simple_77.php @@ -104,14 +104,14 @@ $oldOptions = [ 'sort_order' => 0, 'values' => [ [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 1', 'price' => 3, 'price_type' => 'fixed', 'sku' => '3-1-select', ], [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 2', 'price' => 3, 'price_type' => 'fixed', @@ -127,14 +127,14 @@ $oldOptions = [ 'sort_order' => 0, 'values' => [ [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 1', 'price' => 3, 'price_type' => 'fixed', 'sku' => '4-1-radio', ], [ - 'option_type_id' => -1, + 'option_type_id' => null, 'title' => 'Option 2', 'price' => 3, 'price_type' => 'fixed', diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_shipping_and_invoice_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_shipping_and_invoice_rollback.php new file mode 100644 index 0000000000000000000000000000000000000000..48cf991c848b067b7a3f631b13da812ab0de39b5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_shipping_and_invoice_rollback.php @@ -0,0 +1,6 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +require 'default_rollback.php'; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php index 78342c886de968a02ceed79c215c5dc26ddd9928..f5ae2f2dfd67b5628326149d6d28f055c9d2485f 100755 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php @@ -785,6 +785,7 @@ return [ ['Mage_Core_Model_Config_Fieldset', 'Magento\Core\Model\Fieldset\Config'], ['Mage_Core_Model_Config_Options', 'Magento\Framework\Filesystem'], ['Magento\Framework\App\Dir', 'Magento\Framework\Filesystem'], + ['Magento\Framework\EntityManager\CustomAttributesMapper'], ['Magento\Framework\Filesystem\Adapter\Local', 'Magento\Framework\Filesystem\Driver\File'], ['Magento\Framework\Filesystem\Adapter\Zlib', 'Magento\Framework\Filesystem\Driver\Zlib'], ['Magento\Framework\Filesystem\AdapterInterface'], diff --git a/lib/internal/Magento/Framework/EntityManager/CustomAttributesMapper.php b/lib/internal/Magento/Framework/EntityManager/CustomAttributesMapper.php deleted file mode 100644 index 70cb79f950f28c7f0cefeb98d5e9a278316cccf2..0000000000000000000000000000000000000000 --- a/lib/internal/Magento/Framework/EntityManager/CustomAttributesMapper.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php -/** - * Copyright © 2016 Magento. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Framework\EntityManager; - -/** - * Class CustomAttributesMapper - */ -class CustomAttributesMapper implements MapperInterface -{ - /** - * @var MapperInterface - */ - private $mapper; - - /** - * CustomAttributesMapper constructor. - * - * @param MapperInterface $mapper - */ - public function __construct(MapperInterface $mapper) - { - $this->mapper = $mapper; - } - - /** - * {@inheritdoc} - */ - public function entityToDatabase($entityType, $data) - { - return $this->mapper->entityToDatabase($entityType, $data); - } - - /** - * {@inheritdoc} - * @deprecated - */ - public function databaseToEntity($entityType, $data) - { - return $this->mapper->databaseToEntity($entityType, $data); - } -} diff --git a/lib/internal/Magento/Framework/EntityManager/Db/CreateRow.php b/lib/internal/Magento/Framework/EntityManager/Db/CreateRow.php index f4362e4d4a210defe41a380b30516c93130e6751..5eeb1d948ba39b7e8a1a8e3c942eb8a21009cadf 100644 --- a/lib/internal/Magento/Framework/EntityManager/Db/CreateRow.php +++ b/lib/internal/Magento/Framework/EntityManager/Db/CreateRow.php @@ -50,11 +50,12 @@ class CreateRow { $output = []; foreach ($connection->describeTable($metadata->getEntityTable()) as $column) { - - if ($column['DEFAULT'] == 'CURRENT_TIMESTAMP') { + $columnName = strtolower($column['COLUMN_NAME']); + if ($this->canNotSetTimeStamp($columnName, $column, $data)) { continue; } - if (isset($data[strtolower($column['COLUMN_NAME'])])) { + + if (isset($data[$columnName])) { $output[strtolower($column['COLUMN_NAME'])] = $data[strtolower($column['COLUMN_NAME'])]; } elseif ($column['DEFAULT'] === null) { $output[strtolower($column['COLUMN_NAME'])] = null; @@ -66,6 +67,18 @@ class CreateRow return $output; } + /** + * @param string $columnName + * @param string $column + * @param array $data + * @return bool + */ + private function canNotSetTimeStamp($columnName, $column, array $data) + { + return $column['DEFAULT'] == 'CURRENT_TIMESTAMP' && !isset($data[$columnName]) + && empty($column['NULLABLE']); + } + /** * @param string $entityType * @param array $data diff --git a/lib/internal/Magento/Framework/EntityManager/Db/UpdateRow.php b/lib/internal/Magento/Framework/EntityManager/Db/UpdateRow.php index 3f65774cf10a615d7cfd95b30b06b0fb1ccd4a1e..0c9189261bc45e1b427fc2bffcdde71d27fc8571 100644 --- a/lib/internal/Magento/Framework/EntityManager/Db/UpdateRow.php +++ b/lib/internal/Magento/Framework/EntityManager/Db/UpdateRow.php @@ -50,11 +50,12 @@ class UpdateRow { $output = []; foreach ($connection->describeTable($metadata->getEntityTable()) as $column) { - if ($column['DEFAULT'] == 'CURRENT_TIMESTAMP' || $column['IDENTITY']) { + $columnName = strtolower($column['COLUMN_NAME']); + if ($this->canNotSetTimeStamp($columnName, $column, $data) || $column['IDENTITY']) { continue; } - if (isset($data[strtolower($column['COLUMN_NAME'])])) { + if (isset($data[$columnName])) { $output[strtolower($column['COLUMN_NAME'])] = $data[strtolower($column['COLUMN_NAME'])]; } elseif (!empty($column['NULLABLE'])) { $output[strtolower($column['COLUMN_NAME'])] = null; @@ -67,6 +68,18 @@ class UpdateRow return $output; } + /** + * @param string $columnName + * @param string $column + * @param array $data + * @return bool + */ + private function canNotSetTimeStamp($columnName, $column, array $data) + { + return $column['DEFAULT'] == 'CURRENT_TIMESTAMP' && !isset($data[$columnName]) + && empty($column['NULLABLE']); + } + /** * @param string $entityType * @param array $data diff --git a/lib/internal/Magento/Framework/EntityManager/Test/Unit/Db/UpdateRowTest.php b/lib/internal/Magento/Framework/EntityManager/Test/Unit/Db/UpdateRowTest.php index b22334f4402b1597a7af220cfc168379cde449be..1379ca072bf91898c25a06a796b40f26f2fc2899 100644 --- a/lib/internal/Magento/Framework/EntityManager/Test/Unit/Db/UpdateRowTest.php +++ b/lib/internal/Magento/Framework/EntityManager/Test/Unit/Db/UpdateRowTest.php @@ -63,33 +63,14 @@ class UpdateRowTest extends \PHPUnit_Framework_TestCase ]); } - public function testExecute() + /** + * @dataProvider columnsDataProvider + * @param array $data + * @param array $columns + * @param array $preparedColumns + */ + public function testExecute(array $data, array $columns, array $preparedColumns) { - $data = [ - 'test_link_field' => 1, - 'identified_field' => 'test_identified_field', - 'test_simple' => 'test_value', - ]; - $columns = [ - 'test_nullable' => [ - 'NULLABLE' => true, - 'DEFAULT' => false, - 'IDENTITY' => false, - 'COLUMN_NAME' => 'test_nullable', - ], - 'test_simple' => [ - 'NULLABLE' => true, - 'DEFAULT' => false, - 'IDENTITY' => false, - 'COLUMN_NAME' => 'test_simple', - ], - ]; - $preparedColumns = [ - 'test_identified_field' => null, - 'test_nullable' => null, - 'test_simple' => 'test_value', - ]; - $this->metadataPoolMock->expects($this->once()) ->method('getMetadata') ->with('test') @@ -115,7 +96,78 @@ class UpdateRowTest extends \PHPUnit_Framework_TestCase $this->metadataMock->expects($this->exactly(2)) ->method('getIdentifierField') ->willReturn('test_identified_field'); - + if (empty($data['updated_at'])) { + unset($data['updated_at']); + } $this->assertSame($data, $this->model->execute('test', $data)); } + + /** + * @return array + */ + public function columnsDataProvider() + { + $data = [ + 'test_link_field' => 1, + 'identified_field' => 'test_identified_field', + 'test_simple' => 'test_value', + ]; + $columns = [ + 'test_nullable' => [ + 'NULLABLE' => true, + 'DEFAULT' => false, + 'IDENTITY' => false, + 'COLUMN_NAME' => 'test_nullable', + ], + 'test_simple' => [ + 'NULLABLE' => true, + 'DEFAULT' => false, + 'IDENTITY' => false, + 'COLUMN_NAME' => 'test_simple', + ], + ]; + $preparedColumns = [ + 'test_identified_field' => null, + 'test_nullable' => null, + 'test_simple' => 'test_value', + ]; + + return [ + 'default' => [ + 'data' => $data, + 'columns' => $columns, + 'preparedColumns' => $preparedColumns, + ], + 'empty timestamp field' => [ + 'data' => array_merge($data, ['updated_at' => '']), + 'columns' => array_merge( + $columns, + [ + 'updated_at' => [ + 'NULLABLE' => false, + 'DEFAULT' => 'CURRENT_TIMESTAMP', + 'IDENTITY' => false, + 'COLUMN_NAME' => 'updated_at', + ], + ] + ), + 'preparedColumns' => $preparedColumns, + ], + 'filled timestamp field' => [ + 'data' => array_merge($data, ['updated_at' => '2016-01-01 00:00:00']), + 'columns' => array_merge( + $columns, + [ + 'updated_at' => [ + 'NULLABLE' => false, + 'DEFAULT' => 'CURRENT_TIMESTAMP', + 'IDENTITY' => false, + 'COLUMN_NAME' => 'updated_at', + ], + ] + ), + 'preparedColumns' => array_merge($preparedColumns, ['updated_at' => '2016-01-01 00:00:00']), + ], + ]; + } } diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/html5-schema.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/html5-schema.js index a74a9f12fb2dfe1a0ea5e79b445608016c61462c..f224218f4f731511b8ba13f3ded06b5a73393f4d 100644 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/html5-schema.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/html5-schema.js @@ -46,7 +46,7 @@ define([ }; schema.flowContent = schema.blockContent.concat(schema.phrasingContent, ['style']); - schema.nonEmpty = ['td', 'th', 'iframe', 'video', 'audio', 'object', 'script'].concat(schema.shortEnded); + schema.nonEmpty = ['td', 'th', 'iframe', 'video', 'audio', 'object', 'script', 'i', 'em', 'span'].concat(schema.shortEnded); _.extend(schema, (function (phrasingContent, flowContent) { var validElements = [], diff --git a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js index 4964e6bb14e703f3a0a11a84ab1af78c7e9cfe6e..66b72857d003397b8a756dc6aae9c0de70c58eeb 100755 --- a/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js +++ b/lib/web/mage/adminhtml/wysiwyg/tiny_mce/setup.js @@ -101,6 +101,8 @@ define([ magentoPluginsOptions: magentoPluginsOptions, doctype: '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">', setup: function(ed){ + ed.onPreInit.add(self.onEditorPreInit.bind(self)); + ed.onInit.add(self.onEditorInit.bind(self)); ed.onSubmit.add(function(ed, e) { @@ -285,10 +287,18 @@ define([ } }, - onEditorInit: function (editor) { + /** + * Editor pre-initialise event handler. + */ + onEditorPreInit: function (editor) { this.applySchema(editor); }, + /** + * @deprecated + */ + onEditorInit: function () {}, + onFormValidation: function() { if (tinyMCE.get(this.id)) { $(this.id).value = tinyMCE.get(this.id).getContent();