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/Category.php b/app/code/Magento/Catalog/Model/Category.php index b1f9dbf8c327a46176208cec90f8130065d8400a..9e855e05c7ce388dd52c11829882d5b46f634d02 100644 --- a/app/code/Magento/Catalog/Model/Category.php +++ b/app/code/Magento/Catalog/Model/Category.php @@ -65,7 +65,7 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements */ const TREE_ROOT_ID = 1; - const CACHE_TAG = 'catalog_category'; + const CACHE_TAG = 'cat_c'; /**#@+ * Constants 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.php b/app/code/Magento/Catalog/Model/Product.php index 49df6fd57c323bc1f12f65c590ff39ff4e041123..9e9f18e0113717ad6853ea5d4512661a897e926c 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -58,12 +58,12 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements /** * Product cache tag */ - const CACHE_TAG = 'catalog_product'; + const CACHE_TAG = 'cat_p'; /** * Category product relation cache tag */ - const CACHE_PRODUCT_CATEGORY_TAG = 'catalog_category_product'; + const CACHE_PRODUCT_CATEGORY_TAG = 'cat_c_p'; /** * Product Store Id diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php index 471913dc4888b4a49dd53a99b93f3a6aef15107b..aaf6a5a8b79b0b4988bc1b3caad56ff3ffa2dd66 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php @@ -165,12 +165,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/Type/File.php b/app/code/Magento/Catalog/Model/Product/Option/Type/File.php index d974174ef7088e4a14a9e031e7e266d827ceb13d..70c20a4e314903ce9defb0e40f50c912a83d7307 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/File.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/File.php @@ -5,6 +5,7 @@ */ namespace Magento\Catalog\Model\Product\Option\Type; +use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; use Magento\Framework\Exception\LocalizedException; use Magento\Catalog\Model\Product\Exception as ProductException; @@ -69,17 +70,23 @@ class File extends \Magento\Catalog\Model\Product\Option\Type\DefaultType */ protected $validatorFile; + /** + * @var Filesystem + */ + private $filesystem; + /** * @param \Magento\Checkout\Model\Session $checkoutSession * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Quote\Model\Quote\Item\OptionFactory $itemOptionFactory - * @param \Magento\Catalog\Model\Product\Option\UrlBuilder $urlBuilder - * @param \Magento\Framework\Escaper $escaper * @param \Magento\MediaStorage\Helper\File\Storage\Database $coreFileStorageDatabase * @param File\ValidatorInfo $validatorInfo * @param File\ValidatorFile $validatorFile + * @param \Magento\Catalog\Model\Product\Option\UrlBuilder $urlBuilder + * @param \Magento\Framework\Escaper $escaper * @param array $data - * @throws \Magento\Framework\Exception\FileSystemException + * @param Filesystem $filesystem + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( \Magento\Checkout\Model\Session $checkoutSession, @@ -90,12 +97,15 @@ class File extends \Magento\Catalog\Model\Product\Option\Type\DefaultType \Magento\Catalog\Model\Product\Option\Type\File\ValidatorFile $validatorFile, \Magento\Catalog\Model\Product\Option\UrlBuilder $urlBuilder, \Magento\Framework\Escaper $escaper, - array $data = [] + array $data = [], + Filesystem $filesystem = null ) { $this->_itemOptionFactory = $itemOptionFactory; $this->_urlBuilder = $urlBuilder; $this->_escaper = $escaper; $this->_coreFileStorageDatabase = $coreFileStorageDatabase; + $this->filesystem = $filesystem ?: \Magento\Framework\App\ObjectManager::getInstance()->get(Filesystem::class); + $this->_rootDirectory = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA); $this->validatorInfo = $validatorInfo; $this->validatorFile = $validatorFile; parent::__construct($checkoutSession, $scopeConfig, $data); 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/Block/Product/ListProductTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ListProductTest.php index 38ef297e684267c17c128298147343c979c43f85..f420f140b35820e3cbcebf986ed305217523aab2 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ListProductTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ListProductTest.php @@ -114,8 +114,8 @@ class ListProductTest extends \PHPUnit_Framework_TestCase public function testGetIdentities() { - $productTag = 'catalog_product_1'; - $categoryTag = 'catalog_category_product_1'; + $productTag = 'cat_p_1'; + $categoryTag = 'cat_c_p_1'; $this->productMock->expects($this->once()) ->method('getIdentities') diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php index d030cf456b6b13e9e8dfa240df6252d4016decd4..f0287e05a4b7b3b94f837e9adec3e4a43667e34c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php @@ -32,7 +32,7 @@ class ViewTest extends \PHPUnit_Framework_TestCase $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->productTypeConfig = $this->getMock(\Magento\Catalog\Model\ProductTypes\ConfigInterface::class); $this->registryMock = $this->getMock(\Magento\Framework\Registry::class, [], [], '', false); - $this->view = $helper->getObject( + $this->view = $helper->getObject( \Magento\Catalog\Block\Product\View::class, ['productTypeConfig' => $this->productTypeConfig, 'registry' => $this->registryMock] ); @@ -65,7 +65,7 @@ class ViewTest extends \PHPUnit_Framework_TestCase public function testGetIdentities() { - $productTags = ['catalog_product_1']; + $productTags = ['cat_p_1']; $product = $this->getMock(\Magento\Catalog\Model\Product::class, [], [], '', false); $category = $this->getMock(\Magento\Catalog\Model\Category::class, [], [], '', false); @@ -84,6 +84,6 @@ class ViewTest extends \PHPUnit_Framework_TestCase ] ) ); - $this->assertEquals(['catalog_product_1', 'catalog_category_1'], $this->view->getIdentities()); + $this->assertEquals(['cat_p_1', 'cat_c_1'], $this->view->getIdentities()); } } 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/Model/Product/Option/Type/FileTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Type/FileTest.php index 9b41651863fdb21112d422dd7bda8ee6a97aa09b..6682b295476325f2df099faa183d5edb451cadb5 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Type/FileTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Option/Type/FileTest.php @@ -5,6 +5,12 @@ */ namespace Magento\Catalog\Test\Unit\Model\Product\Option\Type; +use Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Filesystem\DriverPool; + class FileTest extends \PHPUnit_Framework_TestCase { /** @@ -13,7 +19,7 @@ class FileTest extends \PHPUnit_Framework_TestCase protected $objectManager; /** - * @var \Magento\Framework\Filesystem\Directory\ReadInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ReadInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $rootDirectory; @@ -22,14 +28,26 @@ class FileTest extends \PHPUnit_Framework_TestCase */ protected $coreFileStorageDatabase; + /** + * @var Filesystem|\PHPUnit_Framework_MockObject_MockObject + */ + private $filesystemMock; + protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->rootDirectory = $this->getMockBuilder(\Magento\Framework\Filesystem\Directory\ReadInterface::class) + $this->filesystemMock = $this->getMockBuilder(Filesystem::class) ->disableOriginalConstructor() - ->setMethods(['isFile', 'isReadable', 'getAbsolutePath']) - ->getMockForAbstractClass(); + ->getMock(); + + $this->rootDirectory = $this->getMockBuilder(ReadInterface::class) + ->getMock(); + + $this->filesystemMock->expects($this->once()) + ->method('getDirectoryRead') + ->with(DirectoryList::MEDIA, DriverPool::FILE) + ->willReturn($this->rootDirectory); $this->coreFileStorageDatabase = $this->getMock( \Magento\MediaStorage\Helper\File\Storage\Database::class, @@ -48,26 +66,27 @@ class FileTest extends \PHPUnit_Framework_TestCase return $this->objectManager->getObject( \Magento\Catalog\Model\Product\Option\Type\File::class, [ - 'saleableItem' => $this->rootDirectory, - 'priceCurrency' => $this->coreFileStorageDatabase + 'filesystem' => $this->filesystemMock, + 'coreFileStorageDatabase' => $this->coreFileStorageDatabase ] ); } public function testCopyQuoteToOrder() { - $optionMock = $this->getMockBuilder( - \Magento\Catalog\Model\Product\Configuration\Item\Option\OptionInterface::class - )->disableOriginalConstructor()->setMethods(['getValue'])->getMockForAbstractClass(); + $optionMock = $this->getMockBuilder(OptionInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getValue']) + ->getMockForAbstractClass(); $quotePath = '/quote/path/path/uploaded.file'; $orderPath = '/order/path/path/uploaded.file'; $optionMock->expects($this->any()) ->method('getValue') - ->will($this->returnValue(['quote_path' => $quotePath, 'order_path' => $orderPath])); + ->will($this->returnValue(serialize(['quote_path' => $quotePath, 'order_path' => $orderPath]))); - $this->rootDirectory->expects($this->any()) + $this->rootDirectory->expects($this->once()) ->method('isFile') ->with($this->equalTo($quotePath)) ->will($this->returnValue(true)); diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php index b586436b5680d942daa4f9fc6f884138f0a7eeb5..f8e162dff45f390156e63c779ce2c2818d723aa5 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php @@ -708,13 +708,13 @@ class ProductTest extends \PHPUnit_Framework_TestCase return [ 'no changes' => [ - ['catalog_product_1'], + ['cat_p_1'], ['id' => 1, 'name' => 'value', 'category_ids' => [1]], ['id' => 1, 'name' => 'value', 'category_ids' => [1]], ], 'new product' => $this->getNewProductProviderData(), 'status and category change' => [ - [0 => 'catalog_product_1', 1 => 'catalog_category_product_1', 2 => 'catalog_category_product_2'], + [0 => 'cat_p_1', 1 => 'cat_c_p_1', 2 => 'cat_c_p_2'], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 2], [ 'id' => 1, @@ -726,18 +726,18 @@ class ProductTest extends \PHPUnit_Framework_TestCase ], ], 'status change only' => [ - [0 => 'catalog_product_1', 1 => 'catalog_category_product_7'], + [0 => 'cat_p_1', 1 => 'cat_c_p_7'], ['id' => 1, 'name' => 'value', 'category_ids' => [7], 'status' => 1], ['id' => 1, 'name' => 'value', 'category_ids' => [7], 'status' => 2], ], 'status changed, category unassigned' => $this->getStatusAndCategoryChangesData(), 'no status changes' => [ - [0 => 'catalog_product_1'], + [0 => 'cat_p_1'], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], ], 'no stock status changes' => [ - [0 => 'catalog_product_1'], + [0 => 'cat_p_1'], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], [ 'id' => 1, @@ -749,7 +749,7 @@ class ProductTest extends \PHPUnit_Framework_TestCase ], ], 'no stock status data 1' => [ - [0 => 'catalog_product_1'], + [0 => 'cat_p_1'], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], [ 'id' => 1, @@ -760,7 +760,7 @@ class ProductTest extends \PHPUnit_Framework_TestCase ], ], 'no stock status data 2' => [ - [0 => 'catalog_product_1'], + [0 => 'cat_p_1'], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], [ 'id' => 1, @@ -780,7 +780,7 @@ class ProductTest extends \PHPUnit_Framework_TestCase private function getStatusAndCategoryChangesData() { return [ - [0 => 'catalog_product_1', 1 => 'catalog_category_product_5'], + [0 => 'cat_p_1', 1 => 'cat_c_p_5'], ['id' => 1, 'name' => 'value', 'category_ids' => [5], 'status' => 2], [ 'id' => 1, @@ -799,7 +799,7 @@ class ProductTest extends \PHPUnit_Framework_TestCase private function getNewProductProviderData() { return [ - ['catalog_product_1', 'catalog_category_product_1'], + ['cat_p_1', 'cat_c_p_1'], null, [ 'id' => 1, @@ -818,7 +818,7 @@ class ProductTest extends \PHPUnit_Framework_TestCase private function getStatusStockProviderData($extensionAttributesMock) { return [ - [0 => 'catalog_product_1', 1 => 'catalog_category_product_1'], + [0 => 'cat_p_1', 1 => 'cat_c_p_1'], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], [ 'id' => 1, 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..d191f0332f5f28d28f22e981f8ebc74f0c0c8b98 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> @@ -833,4 +834,7 @@ <argument name="collectionProcessor" xsi:type="object">Magento\Eav\Model\Api\SearchCriteria\CollectionProcessor</argument> </arguments> </type> + <type name="Magento\Quote\Model\Quote\Item\ToOrderItem"> + <plugin name="copy_quote_files_to_order" type="Magento\Catalog\Model\Plugin\QuoteItemProductOption"/> + </type> </config> diff --git a/app/code/Magento/Catalog/etc/frontend/di.xml b/app/code/Magento/Catalog/etc/frontend/di.xml index 9b99bd4a781402d662fa6e9c7a17d925c28e6460..ac8c3693e8f3056611a9152e7f259a7e547c4311 100644 --- a/app/code/Magento/Catalog/etc/frontend/di.xml +++ b/app/code/Magento/Catalog/etc/frontend/di.xml @@ -18,9 +18,6 @@ <argument name="fetchStrategy" xsi:type="object">Magento\Catalog\Model\ResourceModel\Category\Collection\FetchStrategy</argument> </arguments> </type> - <type name="Magento\Quote\Model\Quote\Item\ToOrderItem"> - <plugin name="copy_quote_files_to_order" type="Magento\Catalog\Model\Plugin\QuoteItemProductOption"/> - </type> <type name="Magento\Catalog\Model\Indexer\AbstractFlatState"> <arguments> <argument name="isAvailable" xsi:type="boolean">true</argument> 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/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index 2abd8d97ebc01b07bf786173e50969688d4388f1..86b150062d3c057afe500a6d76aac3665690ba87 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -5,6 +5,7 @@ */ namespace Magento\CatalogImportExport\Model\Export; +use Magento\Framework\DB\Ddl\Table; use Magento\ImportExport\Model\Import; use \Magento\Store\Model\Store; use \Magento\CatalogImportExport\Model\Import\Product as ImportProduct; @@ -127,6 +128,13 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity */ protected $_attributeTypes = []; + /** + * Attributes defined by user + * + * @var array + */ + private $userDefinedAttributes = []; + /** * Product collection * @@ -261,7 +269,11 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity */ protected $dateAttrCodes = [ 'special_from_date', - 'special_to_date' + 'special_to_date', + 'news_from_date', + 'news_to_date', + 'custom_design_from', + 'custom_design_to' ]; /** @@ -910,20 +922,24 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity } $fieldName = isset($this->_fieldsMap[$code]) ? $this->_fieldsMap[$code] : $code; - if (in_array($code, $this->dateAttrCodes)) { - $attrValue = $this->_localeDate->formatDateTime( - new \DateTime($attrValue), - \IntlDateFormatter::SHORT, - \IntlDateFormatter::NONE, - null, - date_default_timezone_get() - ); - } else if ($this->_attributeTypes[$code] === 'datetime') { - $attrValue = $this->_localeDate->formatDateTime( - new \DateTime($attrValue), - \IntlDateFormatter::SHORT, - \IntlDateFormatter::SHORT - ); + if ($this->_attributeTypes[$code] == 'datetime') { + if (in_array($code, $this->dateAttrCodes) + || in_array($code, $this->userDefinedAttributes) + ) { + $attrValue = $this->_localeDate->formatDateTime( + new \DateTime($attrValue), + \IntlDateFormatter::SHORT, + \IntlDateFormatter::NONE, + null, + date_default_timezone_get() + ); + } else { + $attrValue = $this->_localeDate->formatDateTime( + new \DateTime($attrValue), + \IntlDateFormatter::SHORT, + \IntlDateFormatter::SHORT + ); + } } if ($storeId != Store::DEFAULT_STORE_ID @@ -1380,6 +1396,9 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity $this->_attributeValues[$attribute->getAttributeCode()] = $this->getAttributeOptions($attribute); $this->_attributeTypes[$attribute->getAttributeCode()] = \Magento\ImportExport\Model\Import::getAttributeType($attribute); + if ($attribute->getIsUserDefined()) { + $this->userDefinedAttributes[] = $attribute->getAttributeCode(); + } } return $this; } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index b37ba530845e579cd5b91970e4255eee1745696d..16df0866135a7f07760134c7aff9a0b15bc6fb01 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -217,7 +217,11 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity */ protected $dateAttrCodes = [ 'special_from_date', - 'special_to_date' + 'special_to_date', + 'news_from_date', + 'news_to_date', + 'custom_design_from', + 'custom_design_to' ]; /** @@ -273,7 +277,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' ]; /** @@ -1699,7 +1703,10 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity if ( 'datetime' == $attribute->getBackendType() - && in_array($attribute->getAttributeCode(), $this->dateAttrCodes) + && ( + in_array($attribute->getAttributeCode(), $this->dateAttrCodes) + || $attribute->getIsUserDefined() + ) ) { $attrValue = $this->dateTime->formatDate($attrValue, false); } else if ('datetime' == $attribute->getBackendType() && strtotime($attrValue)) { @@ -2367,7 +2374,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 + ); } } } @@ -2523,7 +2540,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity $rowData = $this->_customFieldsMapping($rowData); $this->validateRow($rowData, $source->key()); - + $source->next(); } $this->checkUrlKeyDuplicates(); diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Adminhtml/Stock/ItemTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Adminhtml/Stock/ItemTest.php index aef8141e70f340862e7c8f610cdfbf6cd4d6cf8d..dfb8073e718a7f31dd316a88efc7bf9b373dcd60 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Adminhtml/Stock/ItemTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Adminhtml/Stock/ItemTest.php @@ -62,6 +62,6 @@ class ItemTest extends \PHPUnit_Framework_TestCase public function testGetIdentities() { $this->_model->setProductId(1); - $this->assertEquals(['catalog_product_1'], $this->_model->getIdentities()); + $this->assertEquals(['cat_p_1'], $this->_model->getIdentities()); } } diff --git a/app/code/Magento/CatalogRule/view/adminhtml/ui_component/catalog_rule_form.xml b/app/code/Magento/CatalogRule/view/adminhtml/ui_component/catalog_rule_form.xml index af60e0247169bb402d5b6d71be4576ad2ed24566..acfd1efa605c758750d1205662cbdb959e655d15 100644 --- a/app/code/Magento/CatalogRule/view/adminhtml/ui_component/catalog_rule_form.xml +++ b/app/code/Magento/CatalogRule/view/adminhtml/ui_component/catalog_rule_form.xml @@ -149,7 +149,7 @@ <item name="source" xsi:type="string">catalog_rule</item> <item name="dataScope" xsi:type="string">customer_group_ids</item> </item> - <item name="options" xsi:type="object">\Magento\Customer\Model\Customer\Source\GroupSourceInterface</item> + <item name="options" xsi:type="object">\Magento\CatalogRule\Model\Rule\CustomerGroupsOptionsProvider</item> </argument> </field> <field name="from_date"> diff --git a/app/code/Magento/Cms/Model/Block.php b/app/code/Magento/Cms/Model/Block.php index d87f0f0d18884b6c31672fc5fdae32cd47e99c22..b25d433b0fee4f96618a31111940f9d410d9f006 100644 --- a/app/code/Magento/Cms/Model/Block.php +++ b/app/code/Magento/Cms/Model/Block.php @@ -23,7 +23,7 @@ class Block extends AbstractModel implements BlockInterface, IdentityInterface /** * CMS block cache tag */ - const CACHE_TAG = 'cms_block'; + const CACHE_TAG = 'cms_b'; /**#@+ * Block's statuses @@ -36,7 +36,7 @@ class Block extends AbstractModel implements BlockInterface, IdentityInterface /** * @var string */ - protected $_cacheTag = 'cms_block'; + protected $_cacheTag = self::CACHE_TAG; /** * Prefix of model events names diff --git a/app/code/Magento/Cms/Model/Page.php b/app/code/Magento/Cms/Model/Page.php index 20c2e0547193160643feddf1ddded16208e74a57..f98b56d82db03660e8a81353332a089f6739d37d 100644 --- a/app/code/Magento/Cms/Model/Page.php +++ b/app/code/Magento/Cms/Model/Page.php @@ -39,12 +39,12 @@ class Page extends AbstractModel implements PageInterface, IdentityInterface /** * CMS page cache tag */ - const CACHE_TAG = 'cms_page'; + const CACHE_TAG = 'cms_p'; /** * @var string */ - protected $_cacheTag = 'cms_page'; + protected $_cacheTag = self::CACHE_TAG; /** * Prefix of model events names diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php index 45d31dc0e2764c4c41888a23ef0bb7f99836fed3..3c3fa73087e4a17a4c883b370c3a3fd44f7ad645 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/ResetPassword.php @@ -6,6 +6,7 @@ namespace Magento\Customer\Controller\Adminhtml\Index; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\SecurityViolationException; class ResetPassword extends \Magento\Customer\Controller\Adminhtml\Index { @@ -40,6 +41,8 @@ class ResetPassword extends \Magento\Customer\Controller\Adminhtml\Index $messages = $exception->getMessage(); } $this->_addSessionErrorMessages($messages); + } catch (SecurityViolationException $exception) { + $this->messageManager->addErrorMessage($exception->getMessage()); } catch (\Exception $exception) { $this->messageManager->addException( $exception, diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php index b7295fb9ceb4f19dd29fa8fe148552ebb20648e6..44444935973d43e9424242a9931b93121f314bae 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/ResetPasswordTest.php @@ -143,7 +143,7 @@ class ResetPasswordTest extends \PHPUnit_Framework_TestCase $this->messageManager = $this->getMockBuilder( \Magento\Framework\Message\Manager::class )->disableOriginalConstructor()->setMethods( - ['addSuccess', 'addMessage', 'addException'] + ['addSuccess', 'addMessage', 'addException', 'addErrorMessage'] )->getMock(); $this->resultRedirectFactoryMock = $this->getMockBuilder( @@ -332,6 +332,56 @@ class ResetPasswordTest extends \PHPUnit_Framework_TestCase $this->_testedObject->execute(); } + public function testResetPasswordActionSecurityException() + { + $securityText = 'Security violation.'; + $exception = new \Magento\Framework\Exception\SecurityViolationException(__($securityText)); + $customerId = 1; + $email = 'some@example.com'; + $websiteId = 1; + + $this->_request->expects( + $this->once() + )->method( + 'getParam' + )->with( + $this->equalTo('customer_id'), + $this->equalTo(0) + )->will( + $this->returnValue($customerId) + ); + $customer = $this->getMockForAbstractClass( + \Magento\Customer\Api\Data\CustomerInterface::class, + ['getId', 'getEmail', 'getWebsiteId'] + ); + $customer->expects($this->once())->method('getEmail')->will($this->returnValue($email)); + $customer->expects($this->once())->method('getWebsiteId')->will($this->returnValue($websiteId)); + $this->_customerRepositoryMock->expects( + $this->once() + )->method( + 'getById' + )->with( + $customerId + )->will( + $this->returnValue($customer) + ); + $this->_customerAccountManagementMock->expects( + $this->once() + )->method( + 'initiatePasswordReset' + )->willThrowException($exception); + + $this->messageManager->expects( + $this->once() + )->method( + 'addErrorMessage' + )->with( + $this->equalTo($exception->getMessage()) + ); + + $this->_testedObject->execute(); + } + public function testResetPasswordActionCoreExceptionWarn() { $warningText = 'Warning'; 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/Email/Model/Mail/TransportInterfacePlugin.php b/app/code/Magento/Email/Model/Mail/TransportInterfacePlugin.php new file mode 100644 index 0000000000000000000000000000000000000000..02c1fee1b0745efbdee91e025f7b7c4d9b6f03b4 --- /dev/null +++ b/app/code/Magento/Email/Model/Mail/TransportInterfacePlugin.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Email\Model\Mail; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Exception\MailException; +use Magento\Framework\Mail\TransportInterface; +use Magento\Store\Model\ScopeInterface; + +class TransportInterfacePlugin +{ + /** + * Config path to mail sending setting that shows if email communications are disabled + */ + const XML_PATH_SYSTEM_SMTP_DISABLE = 'system/smtp/disable'; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct( + ScopeConfigInterface $scopeConfig + ) { + $this->scopeConfig = $scopeConfig; + } + + /** + * Omit email sending if disabled + * + * @param TransportInterface $subject + * @param \Closure $proceed + * @return void + * @throws MailException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function aroundSendMessage( + TransportInterface $subject, + \Closure $proceed + ) { + if (!$this->scopeConfig->isSetFlag(self::XML_PATH_SYSTEM_SMTP_DISABLE, ScopeInterface::SCOPE_STORE)) { + $proceed(); + } + } +} diff --git a/app/code/Magento/Email/Model/Template.php b/app/code/Magento/Email/Model/Template.php index c281a08ab97b6bd153b77a0803b7d2c21792cc50..c42c4088793d7c429bf246bd3090731751e4ddff 100644 --- a/app/code/Magento/Email/Model/Template.php +++ b/app/code/Magento/Email/Model/Template.php @@ -5,26 +5,11 @@ */ namespace Magento\Email\Model; -use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; /** * Template model * - * Example: - * - * // Loading of template - * \Magento\Email\Model\TemplateFactory $templateFactory - * $templateFactory->create()->load($this->_scopeConfig->getValue( - * 'path_to_email_template_id_config', - * \Magento\Store\Model\ScopeInterface::SCOPE_STORE - * )); - * $variables = array( - * 'someObject' => $this->_coreResourceEmailTemplate - * 'someString' => 'Some string value' - * ); - * $emailTemplate->send('some@domain.com', 'Name Of User', $variables); - * * @method \Magento\Email\Model\ResourceModel\Template _getResource() * @method \Magento\Email\Model\ResourceModel\Template getResource() * @method string getTemplateCode() @@ -63,6 +48,8 @@ class Template extends AbstractTemplate implements \Magento\Framework\Mail\Templ /** * Config path to mail sending setting that shows if email communications are disabled + * @deprecated + * @see \Magento\Email\Model\Mail\TransportInterfacePlugin::XML_PATH_SYSTEM_SMTP_DISABLE */ const XML_PATH_SYSTEM_SMTP_DISABLE = 'system/smtp/disable'; @@ -196,8 +183,7 @@ class Template extends AbstractTemplate implements \Magento\Framework\Mail\Templ */ public function isValidForSend() { - return !$this->scopeConfig->isSetFlag(Template::XML_PATH_SYSTEM_SMTP_DISABLE, ScopeInterface::SCOPE_STORE) - && $this->getSenderName() && $this->getSenderEmail() && $this->getTemplateSubject(); + return $this->getSenderName() && $this->getSenderEmail() && $this->getTemplateSubject(); } /** @@ -344,7 +330,8 @@ class Template extends AbstractTemplate implements \Magento\Framework\Mail\Templ if ($this->_getResource()->checkCodeUsage($this)) { throw new \Magento\Framework\Exception\MailException(__('Duplicate Of Template Name')); } - return parent::beforeSave(); + parent::beforeSave(); + return $this; } /** diff --git a/app/code/Magento/Email/Test/Unit/Model/TemplateTest.php b/app/code/Magento/Email/Test/Unit/Model/TemplateTest.php index f2195a64220e25effc00550af98c93d21e5e0145..3ee95954c0a3af7d122d62631e77f94d54ad5dda 100644 --- a/app/code/Magento/Email/Test/Unit/Model/TemplateTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/TemplateTest.php @@ -5,7 +5,6 @@ */ namespace Magento\Email\Test\Unit\Model; -use Magento\Email\Model\Template\Filter; use Magento\Framework\App\Area; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\TemplateTypesInterface; @@ -426,18 +425,13 @@ class TemplateTest extends \PHPUnit_Framework_TestCase } /** - * @param $isSMTPDisabled bool * @param $senderName string * @param $senderEmail string * @param $templateSubject string * @dataProvider isValidForSendDataProvider */ - public function testIsValidForSend($isSMTPDisabled, $senderName, $senderEmail, $templateSubject, $expectedValue) + public function testIsValidForSend($senderName, $senderEmail, $templateSubject, $expectedValue) { - $this->scopeConfig->expects($this->once()) - ->method('isSetFlag') - ->with('system/smtp/disable', ScopeInterface::SCOPE_STORE) - ->will($this->returnValue($isSMTPDisabled)); $model = $this->getModelMock(['getSenderName', 'getSenderEmail', 'getTemplateSubject']); $model->expects($this->any()) ->method('getSenderName') @@ -455,35 +449,24 @@ class TemplateTest extends \PHPUnit_Framework_TestCase { return [ 'should be valid' => [ - 'isSMTPDisabled' => false, 'senderName' => 'sender name', 'senderEmail' => 'email@example.com', 'templateSubject' => 'template subject', 'expectedValue' => true ], - 'no smtp so not valid' => [ - 'isSMTPDisabled' => true, - 'senderName' => 'sender name', - 'senderEmail' => 'email@example.com', - 'templateSubject' => 'template subject', - 'expectedValue' => false - ], 'no sender name so not valid' => [ - 'isSMTPDisabled' => false, 'senderName' => '', 'senderEmail' => 'email@example.com', 'templateSubject' => 'template subject', 'expectedValue' => false ], 'no sender email so not valid' => [ - 'isSMTPDisabled' => false, 'senderName' => 'sender name', 'senderEmail' => '', 'templateSubject' => 'template subject', 'expectedValue' => false ], 'no subject so not valid' => [ - 'isSMTPDisabled' => false, 'senderName' => 'sender name', 'senderEmail' => 'email@example.com', 'templateSubject' => '', diff --git a/app/code/Magento/Email/etc/di.xml b/app/code/Magento/Email/etc/di.xml index d1477d470abd1d35416e4ba40d246ecd90148c26..380e5e6248fb3f3b6a42108f91a2a4f49b08e263 100644 --- a/app/code/Magento/Email/etc/di.xml +++ b/app/code/Magento/Email/etc/di.xml @@ -59,5 +59,6 @@ </type> <type name="Magento\Framework\Mail\TransportInterface"> <plugin name="WindowsSmtpConfig" type="Magento\Email\Model\Plugin\WindowsSmtpConfig" /> + <plugin name="disableSending" type="Magento\Email\Model\Mail\TransportInterfacePlugin" /> </type> </config> diff --git a/app/code/Magento/Newsletter/Model/Template.php b/app/code/Magento/Newsletter/Model/Template.php index 7b454921b0ddc490a139aa35402d987b6cbdeda9..09ea825c612af13a08636a6d4d302ec65cad9a4f 100644 --- a/app/code/Magento/Newsletter/Model/Template.php +++ b/app/code/Magento/Newsletter/Model/Template.php @@ -174,7 +174,8 @@ class Template extends \Magento\Email\Model\AbstractTemplate public function beforeSave() { $this->validate(); - return parent::beforeSave(); + parent::beforeSave(); + return $this; } /** @@ -238,9 +239,6 @@ class Template extends \Magento\Email\Model\AbstractTemplate */ public function isValidForSend() { - return !$this->scopeConfig->isSetFlag( - \Magento\Email\Model\Template::XML_PATH_SYSTEM_SMTP_DISABLE, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) && $this->getTemplateSenderName() && $this->getTemplateSenderEmail() && $this->getTemplateSubject(); + return $this->getTemplateSenderName() && $this->getTemplateSenderEmail() && $this->getTemplateSubject(); } } diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/TemplateTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/TemplateTest.php index e51d5e0fdb3a75f6abcf79cd6289b398ec0e9391..c8599230652425348ce1b8eea8d0679defa9df16 100644 --- a/app/code/Magento/Newsletter/Test/Unit/Model/TemplateTest.php +++ b/app/code/Magento/Newsletter/Test/Unit/Model/TemplateTest.php @@ -379,4 +379,55 @@ class TemplateTest extends \PHPUnit_Framework_TestCase ], ]; } + + /** + * @param $senderName string + * @param $senderEmail string + * @param $templateSubject string + * @dataProvider isValidForSendDataProvider + */ + public function testIsValidForSend($senderName, $senderEmail, $templateSubject, $expectedValue) + { + $model = $this->getModelMock(['getTemplateSenderName', 'getTemplateSenderEmail', 'getTemplateSubject']); + $model->expects($this->any()) + ->method('getTemplateSenderName') + ->will($this->returnValue($senderName)); + $model->expects($this->any()) + ->method('getTemplateSenderEmail') + ->will($this->returnValue($senderEmail)); + $model->expects($this->any()) + ->method('getTemplateSubject') + ->will($this->returnValue($templateSubject)); + $this->assertEquals($expectedValue, $model->isValidForSend()); + } + + public function isValidForSendDataProvider() + { + return [ + 'should be valid' => [ + 'senderName' => 'sender name', + 'senderEmail' => 'email@example.com', + 'templateSubject' => 'template subject', + 'expectedValue' => true + ], + 'no sender name so not valid' => [ + 'senderName' => '', + 'senderEmail' => 'email@example.com', + 'templateSubject' => 'template subject', + 'expectedValue' => false + ], + 'no sender email so not valid' => [ + 'senderName' => 'sender name', + 'senderEmail' => '', + 'templateSubject' => 'template subject', + 'expectedValue' => false + ], + 'no subject so not valid' => [ + 'senderName' => 'sender name', + 'senderEmail' => 'email@example.com', + 'templateSubject' => '', + 'expectedValue' => false + ], + ]; + } } 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/OrderRepository.php b/app/code/Magento/Sales/Model/OrderRepository.php index 5c14b3fd4433e4404db3921a1fffe5b13935643f..f0477fd76014adae7fefa22c6c16966819856266 100644 --- a/app/code/Magento/Sales/Model/OrderRepository.php +++ b/app/code/Magento/Sales/Model/OrderRepository.php @@ -146,6 +146,16 @@ class OrderRepository implements \Magento\Sales\Api\OrderRepositoryInterface */ public function save(\Magento\Sales\Api\Data\OrderInterface $entity) { + /** @var \Magento\Sales\Api\Data\OrderExtensionInterface $extensionAttributes */ + $extensionAttributes = $entity->getExtensionAttributes(); + if ($entity->getIsNotVirtual() && $extensionAttributes && $extensionAttributes->getShippingAssignments()) { + $shippingAssignments = $extensionAttributes->getShippingAssignments(); + if (!empty($shippingAssignments)) { + $shipping = array_shift($shippingAssignments)->getShipping(); + $entity->setShippingAddress($shipping->getAddress()); + $entity->setShippingMethod($shipping->getMethod()); + } + } $this->metadata->getMapper()->save($entity); $this->registry[$entity->getEntityId()] = $entity; return $this->registry[$entity->getEntityId()]; 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/OrderRepositoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php index 5535ac85095c57e40420e47fa427e0205ebc6aff..f6bc3459c0cc354dd10a945613be75dcefed999d 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php @@ -110,4 +110,40 @@ class OrderRepositoryTest extends \PHPUnit_Framework_TestCase $this->assertEquals($collectionMock, $this->model->getList($searchCriteriaMock)); } + + public function testSave() + { + $mapperMock = $this->getMockBuilder(\Magento\Sales\Model\ResourceModel\Order::class) + ->disableOriginalConstructor() + ->getMock(); + $orderEntity = $this->getMock(\Magento\Sales\Model\Order::class, [], [], '', false); + $extensionAttributes = $this->getMock( + \Magento\Sales\Api\Data\OrderExtension::class, + ['getShippingAssignments'], + [], + '', + false + ); + $shippingAssignment = $this->getMockBuilder(\Magento\Sales\Model\Order\ShippingAssignment::class) + ->disableOriginalConstructor() + ->setMethods(['getShipping']) + ->getMock(); + $shippingMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Shipping::class) + ->disableOriginalConstructor() + ->setMethods(['getAddress', 'getMethod']) + ->getMock(); + $orderEntity->expects($this->once())->method('getExtensionAttributes')->willReturn($extensionAttributes); + $orderEntity->expects($this->once())->method('getIsNotVirtual')->willReturn(true); + $extensionAttributes + ->expects($this->any()) + ->method('getShippingAssignments') + ->willReturn([$shippingAssignment]); + $shippingAssignment->expects($this->once())->method('getShipping')->willReturn($shippingMock); + $shippingMock->expects($this->once())->method('getAddress'); + $shippingMock->expects($this->once())->method('getMethod'); + $this->metadata->expects($this->once())->method('getMapper')->willReturn($mapperMock); + $mapperMock->expects($this->once())->method('save'); + $orderEntity->expects($this->any())->method('getEntityId')->willReturn(1); + $this->model->save($orderEntity); + } } 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/SalesRule/Controller/Adminhtml/Promo/Widget/Chooser.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Widget/Chooser.php new file mode 100644 index 0000000000000000000000000000000000000000..b13ace25f020bfea737f6430916d957713ffa6f9 --- /dev/null +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Widget/Chooser.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesRule\Controller\Adminhtml\Promo\Widget; + +class Chooser extends \Magento\CatalogRule\Controller\Adminhtml\Promo\Widget\Chooser +{ + /** + * Authorization level of a basic admin session + * + * @see _isAllowed() + */ + const ADMIN_RESOURCE = 'Magento_SalesRule::quote'; +} diff --git a/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php b/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php index f85daffd3f9625b08520548c1d96697aad3e58cf..1670819588e6106c13226f6d19ab23edcad93bd0 100644 --- a/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php +++ b/app/code/Magento/SalesRule/Model/Rule/Condition/Product.php @@ -51,4 +51,26 @@ class Product extends \Magento\Rule\Model\Condition\Product\AbstractProduct return parent::validate($product); } + + /** + * Retrieve value element chooser URL + * + * @return string + */ + public function getValueElementChooserUrl() + { + $url = false; + switch ($this->getAttribute()) { + case 'sku': + case 'category_ids': + $url = 'sales_rule/promo_widget/chooser/attribute/' . $this->getAttribute(); + if ($this->getJsFormObject()) { + $url .= '/form/' . $this->getJsFormObject(); + } + break; + default: + break; + } + return $url !== false ? $this->_backendData->getUrl($url) : ''; + } } diff --git a/app/code/Magento/SalesRule/Observer/SalesOrderAfterPlaceObserver.php b/app/code/Magento/SalesRule/Observer/SalesOrderAfterPlaceObserver.php index e352ede867a69638c00598d59fde6455be46c05a..a75947629db5d5a97b054306e7908b74cca300d4 100644 --- a/app/code/Magento/SalesRule/Observer/SalesOrderAfterPlaceObserver.php +++ b/app/code/Magento/SalesRule/Observer/SalesOrderAfterPlaceObserver.php @@ -57,7 +57,7 @@ class SalesOrderAfterPlaceObserver implements ObserverInterface { $order = $observer->getEvent()->getOrder(); - if (!$order || $order->getDiscountAmount() == 0) { + if (!$order || !$order->getAppliedRuleIds()) { return $this; } diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a28ea216eda32ed78b368cf3a7b84c53659904dd --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Rule/Condition/ProductTest.php @@ -0,0 +1,161 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\SalesRule\Test\Unit\Model\Rule\Condition; + +use \Magento\Rule\Model\Condition\Context; +use \Magento\Backend\Helper\Data; +use \Magento\Eav\Model\Config; +use \Magento\Catalog\Model\ProductFactory; +use \Magento\Catalog\Api\ProductRepositoryInterface; +use \Magento\Eav\Model\Entity\AbstractEntity; +use \Magento\Catalog\Model\ResourceModel\Product; +use \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection; +use \Magento\Framework\Locale\FormatInterface; +use \Magento\Eav\Model\Entity\AttributeLoaderInterface; +use \Magento\SalesRule\Model\Rule\Condition\Product as SalesRuleProduct; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ProductTest extends \PHPUnit_Framework_TestCase +{ + /** @var SalesRuleProduct */ + protected $model; + + /** @var Context|\PHPUnit_Framework_MockObject_MockObject */ + protected $contextMock; + + /** @var Data|\PHPUnit_Framework_MockObject_MockObject */ + protected $backendHelperMock; + + /** @var Config|\PHPUnit_Framework_MockObject_MockObject */ + protected $configMock; + + /** @var ProductFactory|\PHPUnit_Framework_MockObject_MockObject */ + protected $productFactoryMock; + + /** @var ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $productRepositoryMock; + + /** @var Product|\PHPUnit_Framework_MockObject_MockObject */ + protected $productMock; + + /** @var Collection|\PHPUnit_Framework_MockObject_MockObject */ + protected $collectionMock; + + /** @var FormatInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $formatMock; + + /** @var AttributeLoaderInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $attributeLoaderInterfaceMock; + + /** + * Setup the test + */ + protected function setUp() + { + $this->contextMock = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->getMock(); + $this->backendHelperMock = $this->getMockBuilder(Data::class) + ->disableOriginalConstructor() + ->getMock(); + $this->configMock = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productFactoryMock = $this->getMockBuilder(ProductFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->productRepositoryMock = $this->getMockBuilder(ProductRepositoryInterface::class) + ->getMockForAbstractClass(); + $this->attributeLoaderInterfaceMock = $this->getMockBuilder(AbstractEntity::class) + ->disableOriginalConstructor() + ->setMethods(['getAttributesByCode']) + ->getMock(); + $this->attributeLoaderInterfaceMock + ->expects($this->any()) + ->method('getAttributesByCode') + ->will($this->returnValue([])); + $this->productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['loadAllAttributes']) + ->getMock(); + $this->productMock + ->expects($this->any()) + ->method('loadAllAttributes') + ->will($this->returnValue($this->attributeLoaderInterfaceMock)); + $this->collectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $this->formatMock = $this->getMockBuilder(FormatInterface::class) + ->getMockForAbstractClass(); + $this->model = new SalesRuleProduct( + $this->contextMock, + $this->backendHelperMock, + $this->configMock, + $this->productFactoryMock, + $this->productRepositoryMock, + $this->productMock, + $this->collectionMock, + $this->formatMock + ); + } + + /** + * @return array + */ + public function testGetValueElementChooserUrlDataProvider() + { + return [ + 'category_ids_without_js_object' => [ + 'category_ids', + 'sales_rule/promo_widget/chooser/attribute/' + ], + 'category_ids_with_js_object' => [ + 'category_ids', + 'sales_rule/promo_widget/chooser/attribute/', + 'jsobject' + ], + 'sku_without_js_object' => [ + 'sku', + 'sales_rule/promo_widget/chooser/attribute/', + 'jsobject' + ], + 'sku_without_with_js_object' => [ + 'sku', + 'sales_rule/promo_widget/chooser/attribute/' + ], + 'none' => [ + '', + '' + ] + ]; + } + + /** + * test getValueElementChooserUrl + * @param string $attribute + * @param string $url + * @param string $jsObject + * @dataProvider testGetValueElementChooserUrlDataProvider + */ + public function testGetValueElementChooserUrl($attribute, $url, $jsObject = '') + { + $this->model->setJsFormObject($jsObject); + $this->model->setAttribute($attribute); + $url .= $this->model->getAttribute(); + $this->backendHelperMock + ->expects($this->any()) + ->method('getUrl') + ->willReturnArgument(0); + + if ($this->model->getJsFormObject()) { + $url .= '/form/' . $this->model->getJsFormObject(); + } + + $this->assertEquals($url, $this->model->getValueElementChooserUrl()); + } +} diff --git a/app/code/Magento/SalesRule/Test/Unit/Observer/SalesOrderAfterPlaceObserverTest.php b/app/code/Magento/SalesRule/Test/Unit/Observer/SalesOrderAfterPlaceObserverTest.php index 0546dce56a3f6d6a27849708daf987c801e14a3c..c3d7e41acdf2801e3a6aaae7eb3f3db24d3a992c 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Observer/SalesOrderAfterPlaceObserverTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Observer/SalesOrderAfterPlaceObserverTest.php @@ -121,10 +121,10 @@ class SalesOrderAfterPlaceObserverTest extends \PHPUnit_Framework_TestCase { $observer = $this->getMock(\Magento\Framework\Event\Observer::class, [], [], '', false); $order = $this->initOrderFromEvent($observer); - $discountAmount = 10; + $ruleIds = null; $order->expects($this->once()) - ->method('getDiscountAmount') - ->will($this->returnValue($discountAmount)); + ->method('getAppliedRuleIds') + ->will($this->returnValue($ruleIds)); $this->ruleFactory->expects($this->never()) ->method('create'); @@ -158,14 +158,10 @@ class SalesOrderAfterPlaceObserverTest extends \PHPUnit_Framework_TestCase $ruleId = 1; $couponId = 1; $customerId = 1; - $discountAmount = 10; - $order->expects($this->once()) + $order->expects($this->exactly(2)) ->method('getAppliedRuleIds') ->will($this->returnValue($ruleId)); - $order->expects($this->once()) - ->method('getDiscountAmount') - ->will($this->returnValue($discountAmount)); $order->expects($this->once()) ->method('getCustomerId') ->will($this->returnValue($customerId)); diff --git a/app/code/Magento/Security/Model/Plugin/AccountManagement.php b/app/code/Magento/Security/Model/Plugin/AccountManagement.php index ba1d4af5618b706bbf2bf009568c526364c47108..c65442ec400209c8c789a7f3103cba6a6db01540 100644 --- a/app/code/Magento/Security/Model/Plugin/AccountManagement.php +++ b/app/code/Magento/Security/Model/Plugin/AccountManagement.php @@ -25,18 +25,26 @@ class AccountManagement */ protected $securityManager; + /** + * @var int + */ + protected $passwordRequestEvent; + /** * AccountManagement constructor. * * @param \Magento\Framework\App\RequestInterface $request * @param SecurityManager $securityManager + * @param int $passwordRequestEvent */ public function __construct( \Magento\Framework\App\RequestInterface $request, - \Magento\Security\Model\SecurityManager $securityManager + \Magento\Security\Model\SecurityManager $securityManager, + $passwordRequestEvent = PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST ) { $this->request = $request; $this->securityManager = $securityManager; + $this->passwordRequestEvent = $passwordRequestEvent; } /** @@ -56,7 +64,7 @@ class AccountManagement $websiteId = null ) { $this->securityManager->performSecurityCheck( - PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST, + $this->passwordRequestEvent, $email ); return [$email, $template, $websiteId]; diff --git a/app/code/Magento/Security/etc/adminhtml/di.xml b/app/code/Magento/Security/etc/adminhtml/di.xml index 4cbd3c3adc567188fef3b0b487d064264d5a1c12..c134638d1266e50442a7bb17b5ea2e7bb6bb42c8 100644 --- a/app/code/Magento/Security/etc/adminhtml/di.xml +++ b/app/code/Magento/Security/etc/adminhtml/di.xml @@ -15,6 +15,11 @@ <type name="Magento\Backend\Controller\Adminhtml\Auth\Login"> <plugin name="security_login_form" type="Magento\Security\Model\Plugin\LoginController" /> </type> + <type name="Magento\Security\Model\Plugin\AccountManagement"> + <arguments> + <argument name="passwordRequestEvent" xsi:type="const">Magento\Security\Model\PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST</argument> + </arguments> + </type> <type name="Magento\Security\Model\SecurityManager"> <arguments> <argument name="securityCheckers" xsi:type="array"> 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/Theme/view/frontend/web/js/view/messages.js b/app/code/Magento/Theme/view/frontend/web/js/view/messages.js index 48507343fe28dc928edf9e6af2038bfe29577216..b707659fb3f4c790eef6df14fd8e6c9d68eeef1d 100644 --- a/app/code/Magento/Theme/view/frontend/web/js/view/messages.js +++ b/app/code/Magento/Theme/view/frontend/web/js/view/messages.js @@ -32,7 +32,7 @@ define([ customerData.set('messages', {}); } - $.cookieStorage.setConf({path: '/', expires: -1}).set('mage-messages', null); + $.cookieStorage.set('mage-messages', ''); } }); }); diff --git a/app/code/Magento/Ui/view/base/web/js/grid/provider.js b/app/code/Magento/Ui/view/base/web/js/grid/provider.js index 92a4a6fd38cc52ce93f4cce45dc823f5fa15d64e..7bca45d0253d047b056e7915d10a7ede491f93b6 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/provider.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/provider.js @@ -43,6 +43,10 @@ define([ .initStorage() .clearData(); + // Load data when there will + // be no more pending assets. + resolver(this.reload, this); + return this; }, @@ -122,9 +126,11 @@ define([ * Handles changes of 'params' object. */ onParamsChange: function () { - this.firstLoad ? - resolver(this.reload, this) : + // It's necessary to make a reload only + // after the initial loading has been made. + if (!this.firstLoad) { this.reload(); + } }, /** 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/Sales/Service/V1/OrderCreateTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderCreateTest.php index 1bee8cdfa17804a7eb91db20d4400717aca50080..1279057089918c2154a42731799063dffc91f329 100755 --- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderCreateTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderCreateTest.php @@ -31,6 +31,9 @@ class OrderCreateTest extends WebapiAbstract $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); } + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ protected function prepareOrder() { /** @var \Magento\Sales\Model\Order $orderBuilder */ @@ -41,6 +44,8 @@ class OrderCreateTest extends WebapiAbstract $orderPaymentFactory = $this->objectManager->get(\Magento\Sales\Model\Order\PaymentFactory::class); /** @var \Magento\Sales\Model\Order\AddressRepository $orderAddressRepository */ $orderAddressRepository = $this->objectManager->get(\Magento\Sales\Model\Order\AddressRepository::class); + /** @var \Magento\Store\Model\StoreManagerInterface $storeManager */ + $storeManager = $this->objectManager->get(\Magento\Store\Model\StoreManagerInterface::class); $order = $orderFactory->create( ['data' => $this->getDataStructure(\Magento\Sales\Api\Data\OrderInterface::class)] @@ -68,6 +73,32 @@ class OrderCreateTest extends WebapiAbstract $order->setCustomerEmail($email); $order->setBaseGrandTotal(100); $order->setGrandTotal(100); + $order->setShippingDescription('Flat Rate - Fixed'); + $order->setIsVirtual(0); + $order->setStoreId($storeManager->getDefaultStoreView()->getId()); + $order->setBaseDiscountAmount(0); + $order->setBaseShippingAmount(5); + $order->setBaseShippingTaxAmount(0); + $order->setBaseSubtotal(100); + $order->setBaseTaxAmount(0); + $order->setBaseToGlobalRate(1); + $order->setBaseToOrderRate(1); + $order->setDiscountAmount(0); + $order->setShippingAmount(0); + $order->setShippingTaxAmount(0); + $order->setStoreToOrderRate(0); + $order->setBaseToOrderRate(0); + $order->setSubtotal(100); + $order->setTaxAmount(0); + $order->setTotalQtyOrdered(1); + $order->setCustomerIsGuest(1); + $order->setCustomerNoteNotify(0); + $order->setCustomerGroupId(0); + $order->setBaseSubtotalInclTax(100); + $order->setWeight(1); + $order->setBaseCurrencyCode('USD'); + $order->setShippingInclTax(5); + $order->setBaseShippingInclTax(5); $this->addProductOption($orderItem); @@ -82,12 +113,39 @@ class OrderCreateTest extends WebapiAbstract $orderAddressBilling->setFirstname('First Name'); $orderAddressBilling->setTelephone('+00(000)-123-45-57'); $orderAddressBilling->setStreet(['Street']); - $orderAddressBilling->setCountryId(1); + $orderAddressBilling->setCountryId('US'); + $orderAddressBilling->setRegion('California'); $orderAddressBilling->setAddressType('billing'); + $orderAddressBilling->setRegionId(12); + + $orderAddressShipping = $orderAddressRepository->create(); + $orderAddressShipping->setCity('City2'); + $orderAddressShipping->setPostcode('12345'); + $orderAddressShipping->setLastname('Last Name2'); + $orderAddressShipping->setFirstname('First Name2'); + $orderAddressShipping->setTelephone('+00(000)-123-45-57'); + $orderAddressShipping->setStreet(['Street']); + $orderAddressShipping->setCountryId('US'); + $orderAddressShipping->setRegion('California'); + $orderAddressShipping->setAddressType('shipping'); + $orderAddressShipping->setRegionId(12); $orderData = $order->getData(); $orderData['billing_address'] = $orderAddressBilling->getData(); $orderData['billing_address']['street'] = ['Street']; + $address = $orderAddressShipping->getData(); + $address['street'] = ['Street']; + $orderData['extension_attributes']['shipping_assignments'] = + [ + [ + 'shipping' => [ + 'address' => $address, + 'method' => 'Flat Rate - Fixed' + ], + 'items' => [$orderItem->getData()], + 'stock_id' => null, + ] + ]; return $orderData; } @@ -172,5 +230,8 @@ class OrderCreateTest extends WebapiAbstract $this->assertTrue((bool)$model->getId()); $this->assertEquals($order['base_grand_total'], $model->getBaseGrandTotal()); $this->assertEquals($order['grand_total'], $model->getGrandTotal()); + $this->assertNotNull($model->getShippingAddress()); + $this->assertTrue((bool)$model->getShippingAddress()->getId()); + $this->assertEquals('Flat Rate - Fixed', $model->getShippingMethod()); } } 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/Catalog/Test/Block/Adminhtml/Category/Edit/CategoryForm.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Edit/CategoryForm.xml index 63535106514f2c893b0f822daeb7350fd7d892a3..dff1a9fd71d865dd52fd6cdd0e05d3e3d000db94 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Edit/CategoryForm.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Category/Edit/CategoryForm.xml @@ -130,8 +130,8 @@ </design> <schedule_design_update> <class>\Magento\Ui\Test\Block\Adminhtml\Section</class> - <selector>//div[contains(@class,'admin__collapsible-block-wrapper')][descendant::input[@name='custom_design_to']]</selector> - <strategy>xpath</strategy> + <selector>[data-index="schedule_design_update"]</selector> + <strategy>css selector</strategy> <fields> <schedule_update_from> <input>datepicker</input> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/TopToolbar.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/TopToolbar.php index 100930ad20aea7a40eff2fcf6b367c85db48a242..3d5e550560e857b8530037637b4f97f7763874e2 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/TopToolbar.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/ProductList/TopToolbar.php @@ -28,7 +28,9 @@ class TopToolbar extends Block */ public function getSelectSortType() { - return $this->_rootElement->find("#sorter")->getElements('option[selected]')[0]->getText(); + $selectedOption = $this->_rootElement->find($this->sorter)->getElements('option[selected]')[0]->getText(); + preg_match('/\w+\s?\w+/', $selectedOption, $matches); + return $matches[0]; } /** @@ -38,7 +40,8 @@ class TopToolbar extends Block */ public function getSortType() { - $content = str_replace("\r", '', $this->_rootElement->find($this->sorter)->getText()); - return explode("\n", $content); + $content = $this->_rootElement->find($this->sorter)->getText(); + preg_match_all('/\w+\s?\w+/', $content, $matches); + return $matches[0]; } } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml index 30f870dea88f063a7278d7380f5d4fe24f508c9f..2fd8c04d1d80dfe643616ee7c550699da91aeece 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Category.xml @@ -67,7 +67,7 @@ <field name="name" xsi:type="string">Category%isolation%</field> <field name="url_key" xsi:type="string">category-%isolation%</field> <field name="parent_id" xsi:type="array"> - <item name="dataset" xsi:type="string">default_subcategory</item> + <item name="dataset" xsi:type="string">default</item> </field> <field name="is_active" xsi:type="string">Yes</field> <field name="include_in_menu" xsi:type="string">Yes</field> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml index 7e6f913dda6e7fd88c0723ccbd5e899ccbeeaafe..22cdab45ac875c6a6987892898e3c8dda3382944 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Category/CreateCategoryEntityTest.xml @@ -39,8 +39,8 @@ <data name="category/data/layout_update_xml" xsi:type="string"><referenceContainer name="catalog.leftnav" remove="true"/></data> <data name="category/data/new_theme" xsi:type="string">Magento Luma</data> <data name="category/data/apply_design_to_products" xsi:type="string">Yes</data> - <data name="category/data/schedule_update_from" xsi:type="string">Jan 10, 2014</data> - <data name="category/data/schedule_update_to" xsi:type="string">Dec 31, 2024</data> + <data name="category/data/schedule_update_from" xsi:type="string">01/10/2014</data> + <data name="category/data/schedule_update_to" xsi:type="string">12/31/2024</data> <constraint name="Magento\Catalog\Test\Constraint\AssertCategorySaveMessage" /> <constraint name="Magento\Catalog\Test\Constraint\AssertCategoryForm" /> </variation> @@ -82,8 +82,8 @@ <data name="category/data/layout_update_xml" xsi:type="string"><referenceContainer name="content.aside" remove="true"/></data> <data name="category/data/new_theme" xsi:type="string">Magento Luma</data> <data name="category/data/apply_design_to_products" xsi:type="string">Yes</data> - <data name="category/data/schedule_update_from" xsi:type="string">Jan 1, 2014</data> - <data name="category/data/schedule_update_to" xsi:type="string">Dec 31, 2024</data> + <data name="category/data/schedule_update_from" xsi:type="string">01/10/2014</data> + <data name="category/data/schedule_update_to" xsi:type="string">12/31/2024</data> <constraint name="Magento\Catalog\Test\Constraint\AssertCategorySaveMessage" /> <constraint name="Magento\Catalog\Test\Constraint\AssertCategoryForm" /> <constraint name="Magento\Catalog\Test\Constraint\AssertCategoryPage" /> 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..a5c1f07c164156a0941b0f8da05c43bdff644b5e 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 @@ -387,9 +388,13 @@ class ProductTest extends \Magento\TestFramework\Indexer\TestCase ); $productAfterImport->load($productBeforeImport->getId()); $this->assertEquals( - @strtotime($row['news_from_date']), + @strtotime(date('m/d/Y', @strtotime($row['news_from_date']))), @strtotime($productAfterImport->getNewsFromDate()) ); + $this->assertEquals( + @strtotime($row['news_to_date']), + @strtotime($productAfterImport->getNewsToDate()) + ); unset($productAfterImport); } unset($productsBeforeImport, $product); @@ -1050,9 +1055,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 +1071,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 +1089,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/Model/Import/_files/products_to_import_with_datetime.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_datetime.csv index ae7e27dbd95c0984548142c93c5d4e35afa0cc20..265e29fab65bb88f469454a7c77185c654e26acc 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_datetime.csv +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_datetime.csv @@ -1,4 +1,4 @@ -sku,news_from_date -simple1,"1/1/2015 20:00" -simple2,"10/8/2012 23:58" -simple3,"12/31/1998 17:30" +sku,news_from_date,news_to_date +simple1,"1/1/2015 20:00","1/2/2015" +simple2,"10/8/2012 23:58","1/3/2015" +simple3,"12/31/1998 17:30","1/4/2015" 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/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f3571c17ddffe72511037e07af3617244831f66b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Adminhtml/Index/ResetPasswordTest.php @@ -0,0 +1,88 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Controller\Adminhtml\Index; + +/** + * ResetPassword controller test. + * + * @magentoAppArea adminhtml + */ +class ResetPasswordTest extends \Magento\TestFramework\TestCase\AbstractBackendController +{ + /** + * Base controller URL + * + * @var string + */ + protected $baseControllerUrl = 'http://localhost/index.php/backend/customer/index/'; + + /** + * Checks reset password functionality with default settings and customer reset request event. + * + * @magentoConfigFixture current_store admin/security/limit_password_reset_requests_method 1 + * @magentoConfigFixture current_store admin/security/min_time_between_password_reset_requests 10 + * @magentoDataFixture Magento/Customer/_files/customer.php + */ + public function testResetPasswordSuccess() + { + $this->passwordResetRequestEventCreate( + \Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST + ); + $this->getRequest()->setPostValue(['customer_id' => '1']); + $this->dispatch('backend/customer/index/resetPassword'); + $this->assertSessionMessages( + $this->equalTo(['The customer will receive an email with a link to reset password.']), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + $this->assertRedirect($this->stringStartsWith($this->baseControllerUrl . 'edit')); + } + + /** + * Checks reset password functionality with default settings, customer and admin reset request events. + * + * @magentoConfigFixture current_store admin/security/limit_password_reset_requests_method 1 + * @magentoConfigFixture current_store admin/security/min_time_between_password_reset_requests 10 + * @magentoConfigFixture current_store contact/email/recipient_email hello@example.com + * @magentoDataFixture Magento/Customer/_files/customer.php + */ + public function testResetPasswordWithSecurityViolationException() + { + $this->passwordResetRequestEventCreate( + \Magento\Security\Model\PasswordResetRequestEvent::CUSTOMER_PASSWORD_RESET_REQUEST + ); + $this->passwordResetRequestEventCreate( + \Magento\Security\Model\PasswordResetRequestEvent::ADMIN_PASSWORD_RESET_REQUEST + ); + $this->getRequest()->setPostValue(['customer_id' => '1']); + $this->dispatch('backend/customer/index/resetPassword'); + $this->assertSessionMessages( + $this->equalTo( + ['Too many password reset requests. Please wait and try again or contact hello@example.com.'] + ), + \Magento\Framework\Message\MessageInterface::TYPE_ERROR + ); + $this->assertRedirect($this->stringStartsWith($this->baseControllerUrl . 'edit')); + } + + /** + * Create and save reset request event with provided request type. + * + * @param int $requestType + */ + private function passwordResetRequestEventCreate($requestType) + { + $passwordResetRequestEventFactory = $this->_objectManager->get( + \Magento\Security\Model\PasswordResetRequestEventFactory::class + ); + $passwordResetRequestEvent = $passwordResetRequestEventFactory->create(); + $passwordResetRequestEvent + ->setRequestType($requestType) + ->setAccountReference('customer@example.com') + ->setCreatedAt(strtotime('now')) + ->setIp('3232249856') + ->save(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Session/SaveHandlerTest.php b/dev/tests/integration/testsuite/Magento/Framework/Session/SaveHandlerTest.php index 7ba149d361f4fcd15c9d5989ff619d9f81b53cc2..40fafeceae44cb8342e03de9cb3a204675c52a88 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Session/SaveHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Session/SaveHandlerTest.php @@ -29,32 +29,20 @@ class SaveHandlerTest extends \PHPUnit_Framework_TestCase */ public function testSetSaveHandler($deploymentConfigHandler, $iniHandler) { - $this->markTestSkipped('MAGETWO-56529'); - // Set expected session.save_handler config - if ($deploymentConfigHandler) { - if ($deploymentConfigHandler !== 'files') { - $expected = 'user'; - } else { - $expected = $deploymentConfigHandler; - } - } else if ($iniHandler) { - $expected = $iniHandler; - } else { - $expected = SaveHandlerInterface::DEFAULT_HANDLER; - } + $expected = $this->getExpectedSaveHandler($deploymentConfigHandler, $iniHandler); // Set ini configuration if ($iniHandler) { ini_set('session.save_handler', $iniHandler); } - + $defaultHandler = ini_get('session.save_handler') ?: SaveHandlerInterface::DEFAULT_HANDLER; /** @var DeploymentConfig | \PHPUnit_Framework_MockObject_MockObject $deploymentConfigMock */ $deploymentConfigMock = $this->getMockBuilder(DeploymentConfig::class) ->disableOriginalConstructor() ->getMock(); $deploymentConfigMock->expects($this->once()) ->method('get') - ->with(Config::PARAM_SESSION_SAVE_METHOD, SaveHandlerInterface::DEFAULT_HANDLER) + ->with(Config::PARAM_SESSION_SAVE_METHOD, $defaultHandler) ->willReturn($deploymentConfigHandler ?: SaveHandlerInterface::DEFAULT_HANDLER); new SaveHandler( @@ -85,4 +73,31 @@ class SaveHandlerTest extends \PHPUnit_Framework_TestCase [false, false], ]; } + + /** + * Retrieve expected session.save_handler + * + * @param string $deploymentConfigHandler + * @param string $iniHandler + * @return string + */ + private function getExpectedSaveHandler($deploymentConfigHandler, $iniHandler) + { + // Set expected session.save_handler config + if ($deploymentConfigHandler) { + if ($deploymentConfigHandler !== 'files') { + $expected = 'user'; + return $expected; + } else { + $expected = $deploymentConfigHandler; + return $expected; + } + } elseif ($iniHandler) { + $expected = $iniHandler; + return $expected; + } else { + $expected = SaveHandlerInterface::DEFAULT_HANDLER; + return $expected; + } + } } 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/internal/Magento/Framework/Pricing/Render/PriceBox.php b/lib/internal/Magento/Framework/Pricing/Render/PriceBox.php index 4048f2d105ac0f1d455e2a07f9ecc4acdc9bd962..ddf14cdc659a26533a60f4e79a25b39e5b94fddd 100644 --- a/lib/internal/Magento/Framework/Pricing/Render/PriceBox.php +++ b/lib/internal/Magento/Framework/Pricing/Render/PriceBox.php @@ -86,7 +86,7 @@ class PriceBox extends Template implements PriceBoxRenderInterface, IdentityInte */ protected function getCacheLifetime() { - return parent::hasCacheLifetime() ? parent::getCacheLifetime() : self::DEFAULT_LIFETIME; + return parent::hasCacheLifetime() ? parent::getCacheLifetime() : null; } /** diff --git a/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/PriceBoxTest.php b/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/PriceBoxTest.php index 86e2dde6359d8a9f759905042d86521c135da04d..9a0dfcd7099ee94718f38f18c4af3ab3ed521109 100644 --- a/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/PriceBoxTest.php +++ b/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/PriceBoxTest.php @@ -241,4 +241,17 @@ class PriceBoxTest extends \PHPUnit_Framework_TestCase { $this->assertEquals($this->rendererPool, $this->model->getRendererPool()); } + + /** + * This tests ensures that protected method getCacheLifetime() returns a null value when cacheLifeTime is not + * explicitly set in the parent block + */ + public function testCacheLifetime() + { + $reflectionClass = new \ReflectionClass(get_class($this->model)); + $methodReflection = $reflectionClass->getMethod('getCacheLifetime'); + $methodReflection->setAccessible(true); + $cacheLifeTime = $methodReflection->invoke($this->model); + $this->assertNull($cacheLifeTime, 'Expected null cache lifetime'); + } } 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();