diff --git a/app/code/Magento/Authorizenet/Model/Source/Cctype.php b/app/code/Magento/Authorizenet/Model/Source/Cctype.php index c6a83e19bd32733781228f873bd964558bd894be..a6e173474ae869f2ed40520ed2ccf8197f752e4d 100644 --- a/app/code/Magento/Authorizenet/Model/Source/Cctype.php +++ b/app/code/Magento/Authorizenet/Model/Source/Cctype.php @@ -17,6 +17,6 @@ class Cctype extends PaymentCctype */ public function getAllowedTypes() { - return ['VI', 'MC', 'AE', 'DI']; + return ['VI', 'MC', 'AE', 'DI', 'JCB', 'DN']; } } diff --git a/app/code/Magento/Authorizenet/etc/config.xml b/app/code/Magento/Authorizenet/etc/config.xml index 70b413b5c1a3936c4efe3542f992b1dfead63999..5c48b88e0829965dd6f9a7d2d66f7373a7bb6990 100644 --- a/app/code/Magento/Authorizenet/etc/config.xml +++ b/app/code/Magento/Authorizenet/etc/config.xml @@ -10,7 +10,7 @@ <payment> <authorizenet_directpost> <active>0</active> - <cctypes>AE,VI,MC,DI</cctypes> + <cctypes>AE,VI,MC,DI,JCB,DN</cctypes> <debug>0</debug> <email_customer>0</email_customer> <login backend_model="Magento\Config\Model\Config\Backend\Encrypted" /> 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/Paypal/view/frontend/web/js/view/payment/method-renderer/in-context/checkout-express.js b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/in-context/checkout-express.js index 97dbf0e81b9da0003018ab6c6975aea3216501b4..8a29f0c3a6ea4f774ffc38c9eeb5e65dd34b9657 100644 --- a/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/in-context/checkout-express.js +++ b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/in-context/checkout-express.js @@ -25,6 +25,9 @@ define( ) { 'use strict'; + // State of PayPal module initialization + var clientInit = false; + return Component.extend({ defaults: { @@ -93,15 +96,26 @@ define( * @returns {Object} */ initClient: function () { + var selector = '#' + this.getButtonId(); + _.each(this.clientConfig, function (fn, name) { if (typeof fn === 'function') { this.clientConfig[name] = fn.bind(this); } }, this); - domObserver.get('#' + this.getButtonId(), function () { - paypalExpressCheckout.checkout.setup(this.merchantId, this.clientConfig); - }.bind(this)); + if (!clientInit) { + domObserver.get(selector, function () { + paypalExpressCheckout.checkout.setup(this.merchantId, this.clientConfig); + clientInit = true; + domObserver.off(selector); + }.bind(this)); + } else { + domObserver.get(selector, function () { + $(selector).on('click', this.clientConfig.click); + domObserver.off(selector); + }.bind(this)); + } return this; }, 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/code/Magento/Vault/Api/PaymentMethodListInterface.php b/app/code/Magento/Vault/Api/PaymentMethodListInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..f9d3e628594ef0f809429f39564271e6f52e7b23 --- /dev/null +++ b/app/code/Magento/Vault/Api/PaymentMethodListInterface.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Vault\Api; + +use Magento\Vault\Model\VaultPaymentInterface; + +/** + * Contains methods to retrieve vault payment methods + * This interface is consistent with \Magento\Payment\Api\PaymentMethodListInterface + * @api + */ +interface PaymentMethodListInterface +{ + /** + * Get list of available vault payments + * @param int $storeId + * @return VaultPaymentInterface[] + */ + public function getList($storeId); + + /** + * Get list of enabled in the configuration vault payments + * @param int $storeId + * @return VaultPaymentInterface[] + */ + public function getActiveList($storeId); +} diff --git a/app/code/Magento/Vault/Model/PaymentMethodList.php b/app/code/Magento/Vault/Model/PaymentMethodList.php new file mode 100644 index 0000000000000000000000000000000000000000..bec073df7971154ee74cd0b794d4cd11d347c832 --- /dev/null +++ b/app/code/Magento/Vault/Model/PaymentMethodList.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Vault\Model; + +use Magento\Payment\Api\Data\PaymentMethodInterface; +use Magento\Payment\Api\PaymentMethodListInterface; +use Magento\Payment\Model\Method\InstanceFactory; +use Magento\Payment\Model\MethodInterface; +use Magento\Vault\Api\PaymentMethodListInterface as VaultPaymentMethodListInterface; + +/** + * Contains methods to retrieve configured vault payments + */ +class PaymentMethodList implements VaultPaymentMethodListInterface +{ + /** + * @var InstanceFactory + */ + private $instanceFactory; + + /** + * @var PaymentMethodListInterface + */ + private $paymentMethodList; + + /** + * PaymentMethodList constructor. + * @param PaymentMethodListInterface $paymentMethodList + * @param InstanceFactory $instanceFactory + */ + public function __construct(PaymentMethodListInterface $paymentMethodList, InstanceFactory $instanceFactory) + { + $this->instanceFactory = $instanceFactory; + $this->paymentMethodList = $paymentMethodList; + } + + /** + * @inheritdoc + */ + public function getList($storeId) + { + return $this->filterList($this->paymentMethodList->getList($storeId)); + } + + /** + * @inheritdoc + */ + public function getActiveList($storeId) + { + return $this->filterList($this->paymentMethodList->getActiveList($storeId)); + } + + /** + * Filter vault methods from payments + * @param PaymentMethodInterface[] $list + * @return VaultPaymentInterface[] + */ + private function filterList(array $list) + { + $paymentMethods = array_map( + function (PaymentMethodInterface $paymentMethod) { + return $this->instanceFactory->create($paymentMethod); + }, + $list + ); + + $availableMethods = array_filter( + $paymentMethods, + function (MethodInterface $methodInstance) { + return $methodInstance instanceof VaultPaymentInterface; + } + ); + return array_values($availableMethods); + } +} diff --git a/app/code/Magento/Vault/Model/Ui/TokensConfigProvider.php b/app/code/Magento/Vault/Model/Ui/TokensConfigProvider.php index 060e16d1c3f1aa14e909dc3662315a35f4da680d..40343c29620e1a5c9ecc6e9779baf77336a09481 100644 --- a/app/code/Magento/Vault/Model/Ui/TokensConfigProvider.php +++ b/app/code/Magento/Vault/Model/Ui/TokensConfigProvider.php @@ -7,10 +7,9 @@ namespace Magento\Vault\Model\Ui; use Magento\Checkout\Model\ConfigProviderInterface; use Magento\Framework\App\ObjectManager; -use Magento\Payment\Api\Data\PaymentMethodInterface; use Magento\Store\Model\StoreManagerInterface; +use Magento\Vault\Api\PaymentMethodListInterface; use Magento\Vault\Model\CustomerTokenManagement; -use Magento\Vault\Model\VaultPaymentInterface; /** * Class ConfigProvider @@ -39,14 +38,9 @@ final class TokensConfigProvider implements ConfigProviderInterface private $customerTokenManagement; /** - * @var \Magento\Payment\Api\PaymentMethodListInterface + * @var PaymentMethodListInterface */ - private $paymentMethodList; - - /** - * @var \Magento\Payment\Model\Method\InstanceFactory - */ - private $paymentMethodInstanceFactory; + private $vaultPaymentList; /** * Constructor @@ -112,7 +106,8 @@ final class TokensConfigProvider implements ConfigProviderInterface private function getComponentProviders() { $providers = []; - $vaultPaymentMethods = $this->getVaultPaymentMethodList(); + $storeId = $this->storeManager->getStore()->getId(); + $vaultPaymentMethods = $this->getVaultPaymentList()->getActiveList($storeId); foreach ($vaultPaymentMethods as $method) { $providerCode = $method->getProviderCode(); @@ -141,60 +136,15 @@ final class TokensConfigProvider implements ConfigProviderInterface } /** - * Get list of active Vault payment methods. - * - * @return VaultPaymentInterface[] - */ - private function getVaultPaymentMethodList() - { - $storeId = $this->storeManager->getStore()->getId(); - - $paymentMethods = array_map( - function (PaymentMethodInterface $paymentMethod) { - return $this->getPaymentMethodInstanceFactory()->create($paymentMethod); - }, - $this->getPaymentMethodList()->getActiveList($storeId) - ); - - $availableMethods = array_filter( - $paymentMethods, - function (\Magento\Payment\Model\MethodInterface $methodInstance) { - return $methodInstance instanceof VaultPaymentInterface; - } - ); - - return $availableMethods; - } - - /** - * Get payment method list. - * - * @return \Magento\Payment\Api\PaymentMethodListInterface - * @deprecated - */ - private function getPaymentMethodList() - { - if ($this->paymentMethodList === null) { - $this->paymentMethodList = ObjectManager::getInstance()->get( - \Magento\Payment\Api\PaymentMethodListInterface::class - ); - } - return $this->paymentMethodList; - } - - /** - * Get payment method instance factory. - * - * @return \Magento\Payment\Model\Method\InstanceFactory + * Get instance of vault payment list instance + * @return PaymentMethodListInterface * @deprecated */ - private function getPaymentMethodInstanceFactory() + private function getVaultPaymentList() { - if ($this->paymentMethodInstanceFactory === null) { - $this->paymentMethodInstanceFactory = ObjectManager::getInstance()->get( - \Magento\Payment\Model\Method\InstanceFactory::class - ); + if ($this->vaultPaymentList === null) { + $this->vaultPaymentList = ObjectManager::getInstance()->get(PaymentMethodListInterface::class); } - return $this->paymentMethodInstanceFactory; + return $this->vaultPaymentList; } } diff --git a/app/code/Magento/Vault/Model/Ui/VaultConfigProvider.php b/app/code/Magento/Vault/Model/Ui/VaultConfigProvider.php index 9cd7b97562df98e309b9ddd182a5ced144b2e555..bc3110a101452b40deb5506bc659d88a5f4e34a1 100644 --- a/app/code/Magento/Vault/Model/Ui/VaultConfigProvider.php +++ b/app/code/Magento/Vault/Model/Ui/VaultConfigProvider.php @@ -8,9 +8,8 @@ namespace Magento\Vault\Model\Ui; use Magento\Checkout\Model\ConfigProviderInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\Session\SessionManagerInterface; -use Magento\Payment\Api\Data\PaymentMethodInterface; use Magento\Store\Model\StoreManagerInterface; -use Magento\Vault\Model\VaultPaymentInterface; +use Magento\Vault\Api\PaymentMethodListInterface; class VaultConfigProvider implements ConfigProviderInterface { @@ -32,14 +31,9 @@ class VaultConfigProvider implements ConfigProviderInterface private $session; /** - * @var \Magento\Payment\Api\PaymentMethodListInterface + * @var PaymentMethodListInterface */ - private $paymentMethodList; - - /** - * @var \Magento\Payment\Model\Method\InstanceFactory - */ - private $paymentMethodInstanceFactory; + private $vaultPaymentList; /** * VaultConfigProvider constructor. @@ -62,9 +56,9 @@ class VaultConfigProvider implements ConfigProviderInterface public function getConfig() { $availableMethods = []; - $vaultPayments = $this->getVaultPaymentMethodList(); - $customerId = $this->session->getCustomerId(); $storeId = $this->storeManager->getStore()->getId(); + $vaultPayments = $this->getVaultPaymentList()->getActiveList($storeId); + $customerId = $this->session->getCustomerId(); foreach ($vaultPayments as $method) { $availableMethods[$method->getCode()] = [ @@ -78,60 +72,15 @@ class VaultConfigProvider implements ConfigProviderInterface } /** - * Get list of active Vault payment methods. - * - * @return VaultPaymentInterface[] - */ - private function getVaultPaymentMethodList() - { - $storeId = $this->storeManager->getStore()->getId(); - - $paymentMethods = array_map( - function (PaymentMethodInterface $paymentMethod) { - return $this->getPaymentMethodInstanceFactory()->create($paymentMethod); - }, - $this->getPaymentMethodList()->getActiveList($storeId) - ); - - $availableMethods = array_filter( - $paymentMethods, - function (\Magento\Payment\Model\MethodInterface $methodInstance) { - return $methodInstance instanceof VaultPaymentInterface; - } - ); - - return $availableMethods; - } - - /** - * Get payment method list. - * - * @return \Magento\Payment\Api\PaymentMethodListInterface - * @deprecated - */ - private function getPaymentMethodList() - { - if ($this->paymentMethodList === null) { - $this->paymentMethodList = ObjectManager::getInstance()->get( - \Magento\Payment\Api\PaymentMethodListInterface::class - ); - } - return $this->paymentMethodList; - } - - /** - * Get payment method instance factory. - * - * @return \Magento\Payment\Model\Method\InstanceFactory + * Get vault payment list instance + * @return PaymentMethodListInterface * @deprecated */ - private function getPaymentMethodInstanceFactory() + private function getVaultPaymentList() { - if ($this->paymentMethodInstanceFactory === null) { - $this->paymentMethodInstanceFactory = ObjectManager::getInstance()->get( - \Magento\Payment\Model\Method\InstanceFactory::class - ); + if ($this->vaultPaymentList === null) { + $this->vaultPaymentList = ObjectManager::getInstance()->get(PaymentMethodListInterface::class); } - return $this->paymentMethodInstanceFactory; + return $this->vaultPaymentList; } } diff --git a/app/code/Magento/Vault/Test/Unit/Model/PaymentMethodListTest.php b/app/code/Magento/Vault/Test/Unit/Model/PaymentMethodListTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9e9d426fa8f3a487335f1f3fd0242833fbcc6e9d --- /dev/null +++ b/app/code/Magento/Vault/Test/Unit/Model/PaymentMethodListTest.php @@ -0,0 +1,74 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Vault\Test\Unit\Model; + +use Magento\Payment\Api\Data\PaymentMethodInterface; +use Magento\Payment\Api\PaymentMethodListInterface; +use Magento\Payment\Model\Method\InstanceFactory; +use Magento\Payment\Model\MethodInterface; +use Magento\Vault\Model\VaultPaymentInterface; +use Magento\Vault\Model\PaymentMethodList; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +class PaymentMethodListTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var PaymentMethodListInterface|MockObject + */ + private $paymentMethodList; + + /** + * @var InstanceFactory|MockObject + */ + private $instanceFactory; + + /** + * @var PaymentMethodList + */ + private $vaultPaymentList; + + protected function setUp() + { + $this->paymentMethodList = $this->getMock(PaymentMethodListInterface::class); + $this->instanceFactory = $this->getMockBuilder(InstanceFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->vaultPaymentList = new PaymentMethodList($this->paymentMethodList, $this->instanceFactory); + } + + /** + * @covers \Magento\Vault\Model\PaymentMethodList::getActiveList + */ + public function testGetActivePaymentList() + { + $storeId = 1; + $vaultPayment = $this->getMock(VaultPaymentInterface::class); + $paymentMethodInterface1 = $this->getMock(PaymentMethodInterface::class); + $paymentMethodInterface2 = $this->getMock(PaymentMethodInterface::class); + $activePayments = [ + $paymentMethodInterface1, + $paymentMethodInterface2 + ]; + + $this->paymentMethodList->expects(static::once()) + ->method('getActiveList') + ->with($storeId) + ->willReturn($activePayments); + + $this->instanceFactory->expects(static::exactly(2)) + ->method('create') + ->willReturnMap([ + [$paymentMethodInterface1, $this->getMock(MethodInterface::class)], + [$paymentMethodInterface2, $vaultPayment] + ]); + + $vaultPayments = $this->vaultPaymentList->getActiveList($storeId); + static::assertCount(1, $vaultPayments); + static::assertInstanceOf(VaultPaymentInterface::class, $vaultPayment); + } +} diff --git a/app/code/Magento/Vault/Test/Unit/Model/Ui/TokensConfigProviderTest.php b/app/code/Magento/Vault/Test/Unit/Model/Ui/TokensConfigProviderTest.php index d5ac3cac9563d4a8d8d4f294eca2dfaab8f44374..34b7c5240497164327cadac60fc00a33eb390c58 100644 --- a/app/code/Magento/Vault/Test/Unit/Model/Ui/TokensConfigProviderTest.php +++ b/app/code/Magento/Vault/Test/Unit/Model/Ui/TokensConfigProviderTest.php @@ -5,11 +5,11 @@ */ namespace Magento\Vault\Test\Unit\Model\Ui; -use Magento\Customer\Model\Session; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Vault\Api\Data\PaymentTokenInterface; +use Magento\Vault\Api\PaymentMethodListInterface; use Magento\Vault\Model\CustomerTokenManagement; use Magento\Vault\Model\Ui\TokensConfigProvider; use Magento\Vault\Model\Ui\TokenUiComponentInterface; @@ -31,25 +31,10 @@ class TokensConfigProviderTest extends \PHPUnit_Framework_TestCase */ private $storeManager; - /** - * @var \Magento\Payment\Api\PaymentMethodListInterface|MockObject - */ - private $paymentMethodList; - - /** - * @var \Magento\Payment\Model\Method\InstanceFactory|MockObject - */ - private $paymentMethodInstanceFactory; - - /** - * @var \Magento\Payment\Api\Data\PaymentMethodInterface|MockObject - */ - private $vaultPayment; - /** * @var VaultPaymentInterface|MockObject */ - private $vaultPaymentInstance; + private $vaultPayment; /** * @var StoreInterface|MockObject @@ -61,6 +46,11 @@ class TokensConfigProviderTest extends \PHPUnit_Framework_TestCase */ private $customerTokenManagement; + /** + * @var PaymentMethodListInterface|MockObject + */ + private $vaultPaymentList; + /** * @var ObjectManager */ @@ -68,20 +58,12 @@ class TokensConfigProviderTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $this->paymentMethodList = $this->getMockBuilder(\Magento\Payment\Api\PaymentMethodListInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - - $this->paymentMethodInstanceFactory = $this->getMockBuilder( - \Magento\Payment\Model\Method\InstanceFactory::class - )->disableOriginalConstructor()->getMock(); - - $this->vaultPayment = $this->getMockForAbstractClass(\Magento\Payment\Api\Data\PaymentMethodInterface::class); - $this->vaultPaymentInstance = $this->getMockForAbstractClass(VaultPaymentInterface::class); + $this->objectManager = new ObjectManager($this); + $this->vaultPaymentList = $this->getMock(PaymentMethodListInterface::class); + $this->vaultPayment = $this->getMockForAbstractClass(VaultPaymentInterface::class); $this->storeManager = $this->getMock(StoreManagerInterface::class); $this->store = $this->getMock(StoreInterface::class); - $this->objectManager = new ObjectManager($this); $this->customerTokenManagement = $this->getMockBuilder(CustomerTokenManagement::class) ->disableOriginalConstructor() ->getMock(); @@ -114,16 +96,12 @@ class TokensConfigProviderTest extends \PHPUnit_Framework_TestCase ->method('getId') ->willReturn($storeId); - $this->paymentMethodList->expects(static::once()) + $this->vaultPaymentList->expects(static::once()) ->method('getActiveList') ->with($storeId) ->willReturn([$this->vaultPayment]); - - $this->paymentMethodInstanceFactory->expects($this->once()) - ->method('create') - ->willReturn($this->vaultPaymentInstance); - $this->vaultPaymentInstance->expects(static::once()) + $this->vaultPayment->expects(static::once()) ->method('getProviderCode') ->willReturn($vaultProviderCode); @@ -153,16 +131,10 @@ class TokensConfigProviderTest extends \PHPUnit_Framework_TestCase $vaultProviderCode => $tokenUiComponentProvider ] ); - - $this->objectManager->setBackwardCompatibleProperty( - $configProvider, - 'paymentMethodList', - $this->paymentMethodList - ); $this->objectManager->setBackwardCompatibleProperty( $configProvider, - 'paymentMethodInstanceFactory', - $this->paymentMethodInstanceFactory + 'vaultPaymentList', + $this->vaultPaymentList ); static::assertEquals($expectedConfig, $configProvider->getConfig()); diff --git a/app/code/Magento/Vault/Test/Unit/Model/Ui/VaultConfigProviderTest.php b/app/code/Magento/Vault/Test/Unit/Model/Ui/VaultConfigProviderTest.php index d00531637b86d4e509662a31ce2da32752ba4a8b..e9d6af3bec356f518fbea6b1ce8be5ab841f78d4 100644 --- a/app/code/Magento/Vault/Test/Unit/Model/Ui/VaultConfigProviderTest.php +++ b/app/code/Magento/Vault/Test/Unit/Model/Ui/VaultConfigProviderTest.php @@ -9,35 +9,20 @@ use Magento\Customer\Model\Session; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Model\StoreManagerInterface; +use Magento\Vault\Api\PaymentMethodListInterface; use Magento\Vault\Model\Ui\VaultConfigProvider; use Magento\Vault\Model\VaultPaymentInterface; use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Class VaultConfigProviderTest - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class VaultConfigProviderTest extends \PHPUnit_Framework_TestCase { - /** - * @var \Magento\Payment\Api\PaymentMethodListInterface|MockObject - */ - private $paymentMethodList; - - /** - * @var \Magento\Payment\Model\Method\InstanceFactory|MockObject - */ - private $paymentMethodInstanceFactory; - - /** - * @var \Magento\Payment\Api\Data\PaymentMethodInterface|MockObject - */ - private $vaultPayment; - /** * @var VaultPaymentInterface|MockObject */ - private $vaultPaymentInstance; + private $vaultPayment; /** * @var Session|MockObject @@ -54,6 +39,11 @@ class VaultConfigProviderTest extends \PHPUnit_Framework_TestCase */ private $storeManager; + /** + * @var PaymentMethodListInterface|MockObject + */ + private $vaultPaymentList; + /** * @var VaultConfigProvider */ @@ -61,33 +51,20 @@ class VaultConfigProviderTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $this->paymentMethodList = $this->getMockBuilder(\Magento\Payment\Api\PaymentMethodListInterface::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - - $this->paymentMethodInstanceFactory = $this->getMockBuilder( - \Magento\Payment\Model\Method\InstanceFactory::class - )->disableOriginalConstructor()->getMock(); - - $this->vaultPayment = $this->getMockForAbstractClass(\Magento\Payment\Api\Data\PaymentMethodInterface::class); - $this->vaultPaymentInstance = $this->getMockForAbstractClass(VaultPaymentInterface::class); + $this->vaultPayment = $this->getMockForAbstractClass(VaultPaymentInterface::class); $this->storeManager = $this->getMockForAbstractClass(StoreManagerInterface::class); $this->store = $this->getMockForAbstractClass(StoreInterface::class); $this->session = $this->getMockBuilder(Session::class) ->disableOriginalConstructor() ->getMock(); + $this->vaultPaymentList = $this->getMock(PaymentMethodListInterface::class); $objectManager = new ObjectManager($this); $this->vaultConfigProvider = new VaultConfigProvider($this->storeManager, $this->session); $objectManager->setBackwardCompatibleProperty( $this->vaultConfigProvider, - 'paymentMethodList', - $this->paymentMethodList - ); - $objectManager->setBackwardCompatibleProperty( - $this->vaultConfigProvider, - 'paymentMethodInstanceFactory', - $this->paymentMethodInstanceFactory + 'vaultPaymentList', + $this->vaultPaymentList ); } @@ -112,26 +89,21 @@ class VaultConfigProviderTest extends \PHPUnit_Framework_TestCase $this->session->expects(static::once()) ->method('getCustomerId') ->willReturn($customerId); - $this->storeManager->expects(static::exactly(2)) + $this->storeManager->expects(static::once()) ->method('getStore') ->willReturn($this->store); - $this->store->expects(static::exactly(2)) + $this->store->expects(static::once()) ->method('getId') ->willReturn($storeId); - $this->paymentMethodList->expects(static::once()) + $this->vaultPaymentList->expects(static::once()) ->method('getActiveList') - ->with($storeId) ->willReturn([$this->vaultPayment]); - $this->paymentMethodInstanceFactory->expects($this->once()) - ->method('create') - ->willReturn($this->vaultPaymentInstance); - - $this->vaultPaymentInstance->expects(static::once()) + $this->vaultPayment->expects(static::once()) ->method('getCode') ->willReturn($vaultPaymentCode); - $this->vaultPaymentInstance->expects($customerId !== null ? static::once() : static::never()) + $this->vaultPayment->expects($customerId !== null ? static::once() : static::never()) ->method('isActive') ->with($storeId) ->willReturn($vaultEnabled); diff --git a/app/code/Magento/Vault/etc/di.xml b/app/code/Magento/Vault/etc/di.xml index e44e1da3e3d61f3c5d230c978b798fa05e84db7e..14354da7e2c528e910a0af6bd15706f25dbeadb4 100644 --- a/app/code/Magento/Vault/etc/di.xml +++ b/app/code/Magento/Vault/etc/di.xml @@ -11,6 +11,7 @@ <preference for="Magento\Vault\Api\Data\PaymentTokenInterface" type="Magento\Vault\Model\PaymentToken"/> <preference for="Magento\Vault\Api\PaymentTokenRepositoryInterface" type="Magento\Vault\Model\PaymentTokenRepository" /> <preference for="Magento\Vault\Api\PaymentTokenManagementInterface" type="Magento\Vault\Model\PaymentTokenManagement" /> + <preference for="Magento\Vault\Api\PaymentMethodListInterface" type="Magento\Vault\Model\PaymentMethodList" /> <preference for="Magento\Vault\Api\Data\PaymentTokenSearchResultsInterface" type="Magento\Framework\Api\SearchResults" /> <preference for="Magento\Vault\Model\Ui\TokenUiComponentInterface" type="Magento\Vault\Model\Ui\TokenUiComponent" /> 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/tests/app/Magento/Usps/Test/TestCase/OnePageCheckoutTest.xml b/dev/tests/functional/tests/app/Magento/Usps/Test/TestCase/OnePageCheckoutTest.xml index 6170b8bbe0616f70fa14cdf50722a96199cf92c3..28c2b0f16239c735c927a8d3f7ef8eb0ebc1b01b 100644 --- a/dev/tests/functional/tests/app/Magento/Usps/Test/TestCase/OnePageCheckoutTest.xml +++ b/dev/tests/functional/tests/app/Magento/Usps/Test/TestCase/OnePageCheckoutTest.xml @@ -28,15 +28,13 @@ </variation> <variation name="OnePageCheckoutUspsTestVariation2" summary="Check Out as Guest using USPS with US shipping origin and UK customer"> <data name="products/0" xsi:type="string">catalogProductSimple::default</data> - <data name="products/1" xsi:type="string">configurableProduct::default</data> - <data name="products/2" xsi:type="string">bundleProduct::bundle_fixed_product</data> <data name="checkoutMethod" xsi:type="string">guest</data> <data name="customer/dataset" xsi:type="string">default</data> <data name="address/dataset" xsi:type="string">UK_address</data> <data name="shippingAddress/dataset" xsi:type="string">UK_address</data> <data name="shipping/shipping_service" xsi:type="string">United States Postal Service</data> - <data name="shipping/shipping_method" xsi:type="string">Priority Mail International</data> - <data name="cart/data/shipping_method" xsi:type="string">Priority Mail International</data> + <data name="shipping/shipping_method" xsi:type="string">Priority Mail Express International Flat Rate Envelope</data> + <data name="cart/data/shipping_method" xsi:type="string">Priority Mail Express International Flat Rate Envelope</data> <data name="payment/method" xsi:type="string">checkmo</data> <data name="configData" xsi:type="string">checkmo, usps, shipping_origin_US_CA</data> <data name="tag" xsi:type="string">test_type:3rd_party_test</data> diff --git a/dev/tests/integration/testsuite/Magento/Braintree/Model/PaymentMethodListTest.php b/dev/tests/integration/testsuite/Magento/Braintree/Model/PaymentMethodListTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0e38adda5498facd9f6213193f536128f2b20c8f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/Model/PaymentMethodListTest.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Braintree\Model; + +use Magento\Braintree\Model\Ui\ConfigProvider; +use Magento\Braintree\Model\Ui\PayPal\ConfigProvider as PayPalConfigProvider; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Vault\Api\PaymentMethodListInterface; +use Magento\Vault\Model\VaultPaymentInterface; + +/** + * Contains tests for vault payment list methods + */ +class PaymentMethodListTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var PaymentMethodListInterface + */ + private $paymentMethodList; + + /** + * @var int + */ + private $storeId; + + protected function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + $this->storeId = $objectManager->get(StoreManagerInterface::class) + ->getStore() + ->getId(); + $this->paymentMethodList = $objectManager->get(PaymentMethodListInterface::class); + } + + /** + * @magentoDataFixture Magento/Braintree/_files/payments.php + */ + public function testGetList() + { + $vaultPayments = $this->paymentMethodList->getList($this->storeId); + + static::assertNotEmpty($vaultPayments); + + $paymentCodes = array_map(function (VaultPaymentInterface $payment) { + return $payment->getCode(); + }, $vaultPayments); + + $expectedCodes = [ + PayPalConfigProvider::PAYPAL_VAULT_CODE, + ConfigProvider::CC_VAULT_CODE + ]; + static::assertNotEmpty(array_intersect($expectedCodes, $paymentCodes)); + } + + /** + * @magentoDataFixture Magento/Braintree/_files/payments.php + */ + public function testGetActiveList() + { + $vaultPayments = $this->paymentMethodList->getActiveList($this->storeId); + + static::assertNotEmpty($vaultPayments); + static::assertCount(1, $vaultPayments); + $payment = array_pop($vaultPayments); + static::assertEquals(PayPalConfigProvider::PAYPAL_VAULT_CODE, $payment->getCode()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Braintree/_files/payments.php b/dev/tests/integration/testsuite/Magento/Braintree/_files/payments.php new file mode 100644 index 0000000000000000000000000000000000000000..99ff4481f2982f5076282161931017893380e491 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Braintree/_files/payments.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +use Magento\Braintree\Model\Ui\PayPal\ConfigProvider; +use Magento\Config\Model\Config; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Config $config */ +$config = $objectManager->get(Config::class); +$config->setDataByPath('payment/' . ConfigProvider::PAYPAL_CODE . '/active', 1); +$config->save(); +$config->setDataByPath('payment/' . ConfigProvider::PAYPAL_VAULT_CODE . '/active', 1); +$config->save(); 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/UnsecureFunctionsUsageTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/UnsecureFunctionsUsageTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6b18eb2b71478463e39e815dec078478c4034a07 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/UnsecureFunctionsUsageTest.php @@ -0,0 +1,167 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Test\Legacy; + +use Magento\Framework\App\Utility\Files; + +/** + * Tests to detect unsecure functions usage + */ +class UnsecureFunctionsUsageTest extends \PHPUnit_Framework_TestCase +{ + /** + * File extensions pattern to search for + * + * @var string + */ + private $fileExtensions = '/\.(php|phtml|js)$/'; + + /** + * Php unsecure functions to detect + * + * @var array + */ + private $phpUnsecureFunctions = [ + 'unserialize', + 'serialize', + 'eval', + 'md5', + 'srand', + 'mt_srand' + ]; + + /** + * JS unsecure functions to detect + * + * @var array + */ + private $jsUnsecureFunctions = []; + + /** + * Detect unsecure functions usage for changed files in whitelist with the exception of blacklist + * + * @return void + */ + public function testUnsecureFunctionsUsage() + { + $invoker = new \Magento\Framework\App\Utility\AggregateInvoker($this); + $invoker( + function ($fileName) { + $result = ''; + $errorMessage = 'The following functions are non secure and should be avoided: ' + . implode(', ', $this->phpUnsecureFunctions) + . ' for PHP'; + if (!empty($this->jsUnsecureFunctions)) { + $errorMessage .= ', and ' + . implode(', ', $this->jsUnsecureFunctions) + . ' for JavaScript'; + } + $errorMessage .= ".\n"; + $regexp = $this->getRegexpByFileExtension(pathinfo($fileName, PATHINFO_EXTENSION)); + if ($regexp) { + $matches = preg_grep( + $regexp, + file($fileName, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) + ); + if (!empty($matches)) { + foreach (array_keys($matches) as $line) { + $result .= $fileName . ':' . ($line + 1) . "\n"; + } + } + $this->assertEmpty($result, $errorMessage . $result); + } + }, + $this->unsecureFunctionsUsageDataProvider() + ); + } + + /** + * Data provider for test + * + * @return array + */ + public function unsecureFunctionsUsageDataProvider() + { + $fileExtensions = $this->fileExtensions; + $directoriesToScan = Files::init()->readLists(__DIR__ . '/_files/security/whitelist.txt'); + $blackListFiles = include __DIR__ . '/_files/security/blacklist.php'; + + $filesToVerify = []; + foreach (glob(__DIR__ . '/../_files/changed_files*') as $listFile) { + $filesToVerify = array_merge( + $filesToVerify, + file($listFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) + ); + } + array_walk( + $filesToVerify, + function (&$file) { + $file = [BP . '/' . $file]; + } + ); + $filesToVerify = array_filter( + $filesToVerify, + function ($path) use ($directoriesToScan, $fileExtensions, $blackListFiles) { + if (!file_exists($path[0])) { + return false; + } + $path = realpath($path[0]); + foreach ($directoriesToScan as $directory) { + $directory = realpath($directory); + if (strpos($path, $directory) === 0) { + if (preg_match($fileExtensions, $path)) { + foreach ($blackListFiles as $blackListFile) { + if (preg_match($blackListFile, $path)) { + return false; + } + } + return true; + } + } + } + return false; + } + ); + return $filesToVerify; + } + + /** + * Get regular expression by file extension + * + * @param string $fileExtension + * @return string|bool + */ + private function getRegexpByFileExtension($fileExtension) + { + $regexp = false; + if ($fileExtension == 'php') { + $regexp = $this->prepareRegexp($this->phpUnsecureFunctions); + } elseif ($fileExtension == 'js') { + $regexp = $this->prepareRegexp($this->jsUnsecureFunctions); + } elseif ($fileExtension == 'phtml') { + $regexp = $this->prepareRegexp($this->phpUnsecureFunctions + $this->jsUnsecureFunctions); + } + return $regexp; + } + + /** + * Prepare regular expression for unsecure function names + * + * @param array $functions + * @return string + */ + private function prepareRegexp(array $functions) + { + if (empty($functions)) { + return ''; + } + $regexArray = []; + foreach ($functions as $function) { + $regexArray[] = '\b' . $function . '\b\('; + } + return '/' . implode('|', $regexArray) . '/i'; + } +} 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/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/blacklist.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/blacklist.php new file mode 100644 index 0000000000000000000000000000000000000000..f055f0a732c664dd8ed109d7ec52b178c537a4d0 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/blacklist.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +return [ + '/Test\/Unit/' +]; diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/whitelist.txt b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/whitelist.txt new file mode 100644 index 0000000000000000000000000000000000000000..2567475de6a035b2a369ac342c076ce1e5cad07d --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/whitelist.txt @@ -0,0 +1,4 @@ +module * / +library * / +setup +pub \ No newline at end of file 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();