diff --git a/app/code/Magento/Catalog/Api/Data/ProductAttributeInterface.php b/app/code/Magento/Catalog/Api/Data/ProductAttributeInterface.php index 4025b2accbb178e7685947ccfdff9605e41fabf3..804f06ff667a3ce26144e60b8c3297d04faaf8ed 100644 --- a/app/code/Magento/Catalog/Api/Data/ProductAttributeInterface.php +++ b/app/code/Magento/Catalog/Api/Data/ProductAttributeInterface.php @@ -12,7 +12,6 @@ namespace Magento\Catalog\Api\Data; interface ProductAttributeInterface extends \Magento\Catalog\Api\Data\EavAttributeInterface { const ENTITY_TYPE_CODE = 'catalog_product'; - const CODE_TIER_PRICE_FIELD_PRICE = 'price'; const CODE_HAS_WEIGHT = 'product_has_weight'; const CODE_SPECIAL_PRICE = 'special_price'; const CODE_PRICE = 'price'; @@ -27,6 +26,9 @@ interface ProductAttributeInterface extends \Magento\Catalog\Api\Data\EavAttribu const CODE_COST = 'cost'; const CODE_SEO_FIELD_URL_KEY = 'url_key'; const CODE_TIER_PRICE = 'tier_price'; + const CODE_TIER_PRICE_FIELD_PRICE = 'price'; + const CODE_TIER_PRICE_FIELD_PERCENTAGE_VALUE = 'percentage_value'; + const CODE_TIER_PRICE_FIELD_VALUE_TYPE = 'value_type'; const CODE_SEO_FIELD_META_DESCRIPTION = 'meta_description'; const CODE_WEIGHT = 'weight'; } diff --git a/app/code/Magento/Catalog/Api/ProductTierPriceManagementInterface.php b/app/code/Magento/Catalog/Api/ProductTierPriceManagementInterface.php index d587aec01ccb6f062e6e5bdb4abe62a5031d519b..f02c3afa5aed9de2235e96648ae8de47b337d54f 100644 --- a/app/code/Magento/Catalog/Api/ProductTierPriceManagementInterface.php +++ b/app/code/Magento/Catalog/Api/ProductTierPriceManagementInterface.php @@ -8,6 +8,7 @@ namespace Magento\Catalog\Api; /** * @api + * @deprecated use ScopedProductTierPriceManagementInterface instead */ interface ProductTierPriceManagementInterface { diff --git a/app/code/Magento/Catalog/Api/ScopedProductTierPriceManagementInterface.php b/app/code/Magento/Catalog/Api/ScopedProductTierPriceManagementInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..8a27480b776b643f86079af240fe179f33ac1573 --- /dev/null +++ b/app/code/Magento/Catalog/Api/ScopedProductTierPriceManagementInterface.php @@ -0,0 +1,45 @@ +<?php +/** + * + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Api; + +/** + * @api + */ +interface ScopedProductTierPriceManagementInterface +{ + /** + * Create tier price for product + * + * @param string $sku + * @param \Magento\Catalog\Api\Data\ProductTierPriceInterface $tierPrice + * @return boolean + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function add($sku, \Magento\Catalog\Api\Data\ProductTierPriceInterface $tierPrice); + + /** + * Remove tier price from product + * + * @param string $sku + * @param \Magento\Catalog\Api\Data\ProductTierPriceInterface $tierPrice + * @return boolean + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function remove($sku, \Magento\Catalog\Api\Data\ProductTierPriceInterface $tierPrice); + + /** + * Get tier price of product + * + * @param string $sku + * @param string $customerGroupId 'all' can be used to specify 'ALL GROUPS' + * @return \Magento\Catalog\Api\Data\ProductTierPriceInterface[] + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function getList($sku, $customerGroupId); +} diff --git a/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Price.php b/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Price.php index 9eb5e2e1fc770a8cb776e39b77d3a886d766b192..b994c787bee7aa76f8a4baa3648bcbb2c0e4ee27 100644 --- a/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Price.php +++ b/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Price.php @@ -5,12 +5,14 @@ */ namespace Magento\Catalog\Model\Config\Source\Product\Options; +use Magento\Catalog\Model\Config\Source\ProductPriceOptionsInterface; + /** * Price types mode source * * @author Magento Core Team <core@magentocommerce.com> */ -class Price implements \Magento\Framework\Option\ArrayInterface +class Price implements ProductPriceOptionsInterface { /** * {@inheritdoc} @@ -20,8 +22,8 @@ class Price implements \Magento\Framework\Option\ArrayInterface public function toOptionArray() { return [ - ['value' => 'fixed', 'label' => __('Fixed')], - ['value' => 'percent', 'label' => __('Percent')] + ['value' => self::VALUE_FIXED, 'label' => __('Fixed')], + ['value' => self::VALUE_PERCENT, 'label' => __('Percent')], ]; } } diff --git a/app/code/Magento/Catalog/Model/Config/Source/ProductPriceOptionsInterface.php b/app/code/Magento/Catalog/Model/Config/Source/ProductPriceOptionsInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..d5d5062bad75832a5d712becbe67442fd7a10ac1 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Config/Source/ProductPriceOptionsInterface.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Model\Config\Source; + +use Magento\Framework\Data\OptionSourceInterface; + +/** + * Interface ProductPriceOptionsInterface + */ +interface ProductPriceOptionsInterface extends OptionSourceInterface +{ + /**#@+ + * Values + */ + const VALUE_FIXED = 'fixed'; + const VALUE_PERCENT = 'percent'; + /**#@-*/ +} diff --git a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php index 720e491cef05e9de600c0db36ce4828ede4e6344..464adb27686d02882a91b1f318e8e998fdcd4a5c 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php @@ -70,6 +70,11 @@ abstract class AbstractAction */ protected $_indexers; + /** + * @var \Magento\Catalog\Model\ResourceModel\Product + */ + private $productResource; + /** * @param \Magento\Framework\App\Config\ScopeConfigInterface $config * @param \Magento\Store\Model\StoreManagerInterface $storeManager @@ -213,12 +218,19 @@ abstract class AbstractAction $table = $this->_defaultIndexerResource->getTable('catalog_product_index_tier_price'); $this->_emptyTable($table); + $tierPriceExpression = $this->_connection->getCheckSql( + 'tp.value = 0', + 'product_price.value * (1 - tp.percentage_value / 100)', + 'tp.value' + ); $websiteExpression = $this->_connection->getCheckSql( 'tp.website_id = 0', - 'ROUND(tp.value * cwd.rate, 4)', - 'tp.value' + 'ROUND(' . $tierPriceExpression . ' * cwd.rate, 4)', + $tierPriceExpression ); $linkField = $this->getProductIdFieldName(); + $priceAttribute = $this->getProductResource()->getAttribute('price'); + $select = $this->_connection->select()->from( ['cpe' => $this->_defaultIndexerResource->getTable('catalog_product_entity')], ['cpe.entity_id'] @@ -238,8 +250,15 @@ abstract class AbstractAction ['cwd' => $this->_defaultIndexerResource->getTable('catalog_product_index_website')], 'cw.website_id = cwd.website_id', [] + )->join( + ['product_price' => $priceAttribute->getBackend()->getTable()], + 'tp.' . $linkField . ' = product_price.' . $linkField, + [] )->where( 'cw.website_id != 0' + )->where( + 'product_price.attribute_id = ?', + $priceAttribute->getAttributeId() )->columns( new \Zend_Db_Expr("MIN({$websiteExpression})") )->group( @@ -462,4 +481,17 @@ abstract class AbstractAction $indexList = $this->_connection->getIndexList($table); return $indexList[$this->_connection->getPrimaryKeyName($table)]['COLUMNS_LIST'][0]; } + + /** + * @return \Magento\Catalog\Model\ResourceModel\Product + * @deprecated + */ + private function getProductResource() + { + if (null === $this->productResource) { + $this->productResource = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Catalog\Model\ResourceModel\Product::class); + } + return $this->productResource; + } } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php index d104dc2ab687f076fb6bbd70ab2abcd65028d0df..3c5cef0e6ce78c10d7feea308ec622f201ff1b8c 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php @@ -129,6 +129,18 @@ abstract class AbstractGroupPrice extends Price return []; } + /** + * Get additional fields + * + * @param array $objectArray + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + protected function getAdditionalFields($objectArray) + { + return []; + } + /** * Whether group price value fixed or percent of original price * @@ -176,9 +188,7 @@ abstract class AbstractGroupPrice extends Price throw new \Magento\Framework\Exception\LocalizedException(__($this->_getDuplicateErrorMessage())); } - if (!$this->isPositiveOrZero($priceRow['price'])) { - return __('Group price must be a number greater than 0.'); - } + $this->validatePrice($priceRow); $duplicates[$compare] = true; } @@ -228,6 +238,20 @@ abstract class AbstractGroupPrice extends Price return true; } + /** + * @param array $priceRow + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + protected function validatePrice(array $priceRow) + { + if (!isset($priceRow['price']) || !$this->isPositiveOrZero($priceRow['price'])) { + throw new \Magento\Framework\Exception\LocalizedException( + __('Group price must be a number greater than 0.') + ); + } + } + /** * Prepare group prices data for website * @@ -270,37 +294,70 @@ abstract class AbstractGroupPrice extends Price */ public function afterLoad($object) { - $storeId = $object->getStoreId(); + $data = $this->_getResource()->loadPriceData( + $object->getData($this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField()), + $this->getWebsiteId($object->getStoreId()) + ); + $this->setPriceData($object, $data); + + return $this; + } + + /** + * @param int $storeId + * @return int|null + */ + private function getWebsiteId($storeId) + { $websiteId = null; if ($this->getAttribute()->isScopeGlobal()) { $websiteId = 0; } elseif ($storeId) { $websiteId = $this->_storeManager->getStore($storeId)->getWebsiteId(); } + return $websiteId; + } - $data = $this->_getResource()->loadPriceData( - $object->getData($this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField()), - $websiteId - ); - foreach ($data as $k => $v) { - $data[$k]['website_price'] = $v['price']; - if ($v['all_groups']) { - $data[$k]['cust_group'] = $this->_groupManagement->getAllCustomersGroup()->getId(); - } - } - + /** + * @param \Magento\Catalog\Model\Product $object + * @param array $priceData + */ + public function setPriceData($object, $priceData) + { + $priceData = $this->modifyPriceData($object, $priceData); + $websiteId = $this->getWebsiteId($object->getStoreId()); if (!$object->getData('_edit_mode') && $websiteId) { - $data = $this->preparePriceData($data, $object->getTypeId(), $websiteId); + $priceData = $this->preparePriceData($priceData, $object->getTypeId(), $websiteId); } - $object->setData($this->getAttribute()->getName(), $data); - $object->setOrigData($this->getAttribute()->getName(), $data); + $object->setData($this->getAttribute()->getName(), $priceData); + $object->setOrigData($this->getAttribute()->getName(), $priceData); $valueChangedKey = $this->getAttribute()->getName() . '_changed'; $object->setOrigData($valueChangedKey, 0); $object->setData($valueChangedKey, 0); + } - return $this; + /** + * Perform price modification + * + * @param \Magento\Catalog\Model\Product $object + * @param array $data + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + protected function modifyPriceData($object, $data) + { + /** @var array $priceItem */ + foreach ($data as $key => $priceItem) { + if (isset($priceItem['price']) && $priceItem['price'] > 0) { + $data[$key]['website_price'] = $priceItem['price']; + } + if ($priceItem['all_groups']) { + $data[$key]['cust_group'] = $this->_groupManagement->getAllCustomersGroup()->getId(); + } + } + return $data; } /** @@ -372,13 +429,13 @@ abstract class AbstractGroupPrice extends Price $useForAllGroups = $data['cust_group'] == $this->_groupManagement->getAllCustomersGroup()->getId(); $customerGroupId = !$useForAllGroups ? $data['cust_group'] : 0; - $new[$key] = array_merge( + $this->getAdditionalFields($data), [ 'website_id' => $data['website_id'], 'all_groups' => $useForAllGroups ? 1 : 0, 'customer_group_id' => $customerGroupId, - 'value' => $data['price'], + 'value' => isset($data['price']) ? $data['price'] : null, ], $this->_getAdditionalUniqueFields($data) ); @@ -412,14 +469,7 @@ abstract class AbstractGroupPrice extends Price } if (!empty($update)) { - foreach ($update as $k => $v) { - if ($old[$k]['price'] != $v['value']) { - $price = new \Magento\Framework\DataObject(['value_id' => $old[$k]['price_id'], 'value' => $v['value']]); - $this->_getResource()->savePriceData($price); - - $isChanged = true; - } - } + $isChanged = $this->updateValues($update, $old); } if ($isChanged) { @@ -430,6 +480,29 @@ abstract class AbstractGroupPrice extends Price return $this; } + /** + * @param array $valuesToUpdate + * @param array $oldValues + * @return boolean + */ + protected function updateValues(array $valuesToUpdate, array $oldValues) + { + $isChanged = false; + foreach ($valuesToUpdate as $key => $value) { + if ($oldValues[$key]['price'] != $value['value']) { + $price = new \Magento\Framework\DataObject( + [ + 'value_id' => $oldValues[$key]['price_id'], + 'value' => $value['value'] + ] + ); + $this->_getResource()->savePriceData($price); + $isChanged = true; + } + } + return $isChanged; + } + /** * Retrieve data for update attribute * diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php index fc0fc07d4d524db04afd4b18b304e1e074980634..480f8e8942e87f200ce3829641ea026d351b1967 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Tierprice.php @@ -75,6 +75,18 @@ class Tierprice extends \Magento\Catalog\Model\Product\Attribute\Backend\GroupPr return $uniqueFields; } + /** + * @inheritdoc + */ + protected function getAdditionalFields($objectArray) + { + $percentageValue = $this->getPercentage($objectArray); + return [ + 'value' => $percentageValue ? null : $objectArray['price'], + 'percentage_value' => $percentageValue ?: null, + ]; + } + /** * Error message when duplicates * @@ -105,4 +117,89 @@ class Tierprice extends \Magento\Catalog\Model\Product\Attribute\Backend\GroupPr { return false; } + + /** + * @inheritdoc + */ + public function validate($object) + { + $attribute = $this->getAttribute(); + $priceRows = $object->getData($attribute->getName()); + $priceRows = array_filter((array)$priceRows); + + foreach ($priceRows as $priceRow) { + $percentage = $this->getPercentage($priceRow); + if ($percentage !== null && (!$this->isPositiveOrZero($percentage) || $percentage > 100)) { + throw new \Magento\Framework\Exception\LocalizedException( + __('Percentage value must be a number between 0 and 100.') + ); + } + } + + return parent::validate($object); + } + + /** + * @inheritdoc + */ + protected function validatePrice(array $priceRow) + { + if (!$this->getPercentage($priceRow)) { + parent::validatePrice($priceRow); + } + } + + /** + * @inheritdoc + */ + protected function modifyPriceData($object, $data) + { + $data = parent::modifyPriceData($object, $data); + foreach ($data as $key => $tierPrice) { + if ($this->getPercentage($tierPrice)) { + $data[$key]['website_price'] = $object->getPrice() * (1 - $this->getPercentage($tierPrice) / 100); + } + } + return $data; + } + + /** + * @param array $valuesToUpdate + * @param array $oldValues + * @return boolean + */ + protected function updateValues(array $valuesToUpdate, array $oldValues) + { + $isChanged = false; + foreach ($valuesToUpdate as $key => $value) { + if ($oldValues[$key]['price'] != $value['value'] + || $this->getPercentage($oldValues[$key]) != $this->getPercentage($value) + ) { + $price = new \Magento\Framework\DataObject( + [ + 'value_id' => $oldValues[$key]['price_id'], + 'value' => $value['value'], + 'percentage_value' => $this->getPercentage($value) + ] + ); + $this->_getResource()->savePriceData($price); + + $isChanged = true; + } + } + return $isChanged; + } + + /** + * Check whether price has percentage value. + * + * @param array $priceRow + * @return null + */ + private function getPercentage($priceRow) + { + return isset($priceRow['percentage_value']) && is_numeric($priceRow['percentage_value']) + ? $priceRow['percentage_value'] + : null; + } } diff --git a/app/code/Magento/Catalog/Model/Product/ScopedTierPriceManagement.php b/app/code/Magento/Catalog/Model/Product/ScopedTierPriceManagement.php new file mode 100644 index 0000000000000000000000000000000000000000..f08702bdd73c426533e8656894bab2e161c2d5a3 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/ScopedTierPriceManagement.php @@ -0,0 +1,156 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Model\Product; + +use Magento\Catalog\Api\Data\ProductTierPriceInterface; +use Magento\Catalog\Api\ScopedProductTierPriceManagementInterface; +use Magento\Store\Model\ScopeInterface; + +class ScopedTierPriceManagement implements ScopedProductTierPriceManagementInterface +{ + /** + * @var \Magento\Catalog\Api\ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var \Magento\Store\Model\StoreManagerInterface + */ + private $storeManager; + + /** + * @var \Magento\Framework\App\Config\ScopeConfigInterface + */ + private $config; + + /** + * @var PriceModifier + */ + private $priceModifier; + + /** + * @var TierPriceManagement + */ + private $tierPriceManagement; + + /** + * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param \Magento\Framework\App\Config\ScopeConfigInterface $config + * @param PriceModifier $priceModifier + * @param TierPriceManagement $tierPriceManagement + */ + public function __construct( + \Magento\Catalog\Api\ProductRepositoryInterface $productRepository, + \Magento\Store\Model\StoreManagerInterface $storeManager, + \Magento\Framework\App\Config\ScopeConfigInterface $config, + PriceModifier $priceModifier, + TierPriceManagement $tierPriceManagement + ) { + $this->productRepository = $productRepository; + $this->storeManager = $storeManager; + $this->priceModifier = $priceModifier; + $this->config = $config; + $this->tierPriceManagement = $tierPriceManagement; + } + + /** + * {@inheritdoc} + */ + public function add($sku, ProductTierPriceInterface $tierPrice) + { + $product = $this->productRepository->get($sku, ['edit_mode' => true]); + $product->setTierPrices( + $this->prepareTierPrices($product->getTierPrices(), $tierPrice) + ); + try { + $this->productRepository->save($product); + } catch (\Exception $e) { + throw new \Magento\Framework\Exception\CouldNotSaveException(__('Could not save group price')); + } + return true; + } + + /** + * @param array $tierPrices + * @param ProductTierPriceInterface $tierPrice + * @return ProductTierPriceInterface[]|null + */ + private function prepareTierPrices(array $tierPrices, ProductTierPriceInterface $tierPrice) + { + $this->validate($tierPrice); + $websiteId = $this->getWebsiteId(); + + foreach ($tierPrices as $index => $item) { + $tierPriceWebsite = $tierPrice->getExtensionAttributes() + ? $tierPrice->getExtensionAttributes()->getWebsiteId() + : 0; + + if ($item->getCustomerGroupId() == $tierPrice->getCustomerGroupId() + && $websiteId == $tierPriceWebsite + && $item->getQty() == $tierPrice->getQty() + ) { + unset($tierPrices[$index]); + break; + } + } + + $tierPrices[] = $tierPrice; + return $tierPrices; + } + + /** + * @return int + */ + private function getWebsiteId() + { + $websiteIdentifier = 0; + $value = $this->config->getValue('catalog/price/scope', ScopeInterface::SCOPE_WEBSITE); + if ($value != 0) { + $websiteIdentifier = $this->storeManager->getWebsite()->getId(); + } + + return $websiteIdentifier; + } + + /** + * @param ProductTierPriceInterface $tierPrice + * @throws \Magento\Framework\Exception\InputException + * @return void + */ + private function validate(ProductTierPriceInterface $tierPrice) + { + $data = ['qty' => $tierPrice->getQty(), 'price' => $tierPrice->getValue()]; + foreach ($data as $value) { + if (!is_float($value) || $value <= 0) { + throw new \Magento\Framework\Exception\InputException(__('Please provide valid data')); + } + } + } + + /** + * {@inheritdoc} + */ + public function remove($sku, ProductTierPriceInterface $tierPrice) + { + $product = $this->productRepository->get($sku, ['edit_mode' => true]); + $this->priceModifier->removeTierPrice( + $product, + $tierPrice->getCustomerGroupId(), + $tierPrice->getQty(), + $this->getWebsiteId() + ); + return true; + } + + /** + * {@inheritdoc} + */ + public function getList($sku, $customerGroupId) + { + return $this->tierPriceManagement->getList($sku, $customerGroupId); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/TierPrice.php b/app/code/Magento/Catalog/Model/Product/TierPrice.php index 131280af1b1a428981d1c1f30c523a6dc940757d..e40b24631359c9874fe900bb171c13cd74a2e05d 100644 --- a/app/code/Magento/Catalog/Model/Product/TierPrice.php +++ b/app/code/Magento/Catalog/Model/Product/TierPrice.php @@ -1,6 +1,5 @@ <?php /** - * * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ @@ -78,8 +77,6 @@ class TierPrice extends \Magento\Framework\Model\AbstractExtensibleModel impleme /** * {@inheritdoc} - * - * @return \Magento\Catalog\Api\Data\ProductTierPriceExtensionInterface|null */ public function getExtensionAttributes() { @@ -88,9 +85,6 @@ class TierPrice extends \Magento\Framework\Model\AbstractExtensibleModel impleme /** * {@inheritdoc} - * - * @param \Magento\Catalog\Api\Data\ProductTierPriceExtensionInterface $extensionAttributes - * @return $this */ public function setExtensionAttributes( \Magento\Catalog\Api\Data\ProductTierPriceExtensionInterface $extensionAttributes diff --git a/app/code/Magento/Catalog/Model/Product/Type/Price.php b/app/code/Magento/Catalog/Model/Product/Type/Price.php index 20d25b9e5afcbc414f4abbe3af74328dce95770d..af15e049203f3f6aae27ef2c6ff071d49cdab8cd 100644 --- a/app/code/Magento/Catalog/Model/Product/Type/Price.php +++ b/app/code/Magento/Catalog/Model/Product/Type/Price.php @@ -10,6 +10,7 @@ use Magento\Catalog\Model\Product; use Magento\Customer\Api\GroupManagementInterface; use Magento\Framework\Pricing\PriceCurrencyInterface; use Magento\Store\Model\Store; +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; /** * Product type price model @@ -80,6 +81,11 @@ class Price */ protected $config; + /** + * @var ProductTierPriceExtensionFactory + */ + private $tierPriceExtensionFactory; + /** * Price constructor. * @param \Magento\CatalogRule\Model\ResourceModel\RuleFactory $ruleFactory @@ -354,7 +360,8 @@ class Price $tierPrices = $this->getExistingPrices($product, 'tier_price'); foreach ($tierPrices as $price) { /** @var \Magento\Catalog\Api\Data\ProductTierPriceInterface $tierPrice */ - $tierPrice = $this->tierPriceFactory->create(); + $tierPrice = $this->tierPriceFactory->create() + ->setExtensionAttributes($this->getTierPriceExtensionAttributes()); $tierPrice->setCustomerGroupId($price['cust_group']); if (array_key_exists('website_price', $price)) { $value = $price['website_price']; @@ -363,11 +370,29 @@ class Price } $tierPrice->setValue($value); $tierPrice->setQty($price['price_qty']); + if (isset($price['percentage_value'])) { + $tierPrice->getExtensionAttributes()->setPercentageValue($price['percentage_value']); + } + $websiteId = isset($price['website_id']) ? $price['website_id'] : $this->getWebsiteForPriceScope(); + $tierPrice->getExtensionAttributes()->setWebsiteId($websiteId); $prices[] = $tierPrice; } return $prices; } + /** + * @deprecated + * @return \Magento\Catalog\Api\Data\ProductTierPriceExtensionInterface + */ + private function getTierPriceExtensionAttributes() + { + if (!$this->tierPriceExtensionFactory) { + $this->tierPriceExtensionFactory = \Magento\Framework\App\ObjectManager::getInstance() + ->get(ProductTierPriceExtensionFactory::class); + } + return $this->tierPriceExtensionFactory->create(); + } + /** * Sets list of product tier prices * @@ -382,19 +407,24 @@ class Price return $this; } - $websiteId = $this->getWebsiteForPriceScope(); $allGroupsId = $this->getAllCustomerGroupsId(); + $websiteId = $this->getWebsiteForPriceScope(); // build the new array of tier prices $prices = []; foreach ($tierPrices as $price) { + $extensionAttributes = $price->getExtensionAttributes(); + $websiteId = $extensionAttributes && $extensionAttributes->getWebsiteId() + ? $extensionAttributes->getWebsiteId() + : $websiteId; $prices[] = [ 'website_id' => $websiteId, 'cust_group' => $price->getCustomerGroupId(), 'website_price' => $price->getValue(), 'price' => $price->getValue(), 'all_groups' => ($price->getCustomerGroupId() == $allGroupsId), - 'price_qty' => $price->getQty() + 'price_qty' => $price->getQty(), + 'percentage_value' => $extensionAttributes ? $extensionAttributes->getPercentageValue() : null ]; } $product->setData('tier_price', $prices); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php index 677d048de6a7f596014887971db414aad391614b..717056ddde02f1cff0bff978eb9bfb7652fa19e3 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Attribute/Backend/GroupPrice/AbstractGroupPrice.php @@ -22,8 +22,21 @@ abstract class AbstractGroupPrice extends \Magento\Framework\Model\ResourceModel */ public function loadPriceData($productId, $websiteId = null) { - $connection = $this->getConnection(); + $select = $this->getSelect($websiteId); + $productIdFieldName = $this->getProductIdFieldName(); + $select->where("{$productIdFieldName} = ?", $productId); + + $this->_loadPriceDataSelect($select); + return $this->getConnection()->fetchAll($select); + } + + /** + * @param int|null $websiteId + * @return \Magento\Framework\DB\Select + */ + public function getSelect($websiteId = null) + { $columns = [ 'price_id' => $this->getIdFieldName(), 'website_id' => 'website_id', @@ -34,12 +47,8 @@ abstract class AbstractGroupPrice extends \Magento\Framework\Model\ResourceModel $columns = $this->_loadPriceDataColumns($columns); - $productIdFieldName = $this->getProductIdFieldName(); - $select = $connection->select() - ->from($this->getMainTable(), $columns) - ->where("{$productIdFieldName} = ?", $productId); - - $this->_loadPriceDataSelect($select); + $select = $this->getConnection()->select() + ->from($this->getMainTable(), $columns); if ($websiteId !== null) { if ($websiteId == '0') { @@ -48,8 +57,7 @@ abstract class AbstractGroupPrice extends \Magento\Framework\Model\ResourceModel $select->where('website_id IN(?)', [0, $websiteId]); } } - - return $connection->fetchAll($select); + return $select; } /** diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Attribute/Backend/Tierprice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Attribute/Backend/Tierprice.php index e75442637b9bb09cbcb8a235cd9ffb4abc415402..0f17ecbf98e4cec5b5fd8eebc84dea07da5dd9e5 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Attribute/Backend/Tierprice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Attribute/Backend/Tierprice.php @@ -34,6 +34,7 @@ class Tierprice extends AbstractGroupPrice { $columns = parent::_loadPriceDataColumns($columns); $columns['price_qty'] = 'qty'; + $columns['percentage_value'] = 'percentage_value'; return $columns; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index f53c1b29f8bd0d83680520cde33aea1bec1e310d..b2b20b9d223e223a093d7f6ae222e19631084528 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -2090,12 +2090,13 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac if ($this->getFlag('tier_price_added')) { return $this; } + $linkField = $this->getConnection()->getAutoIncrementField($this->getTable('catalog_product_entity')); $tierPrices = []; $productIds = []; foreach ($this->getItems() as $item) { - $productIds[] = $item->getId(); - $tierPrices[$item->getId()] = []; + $productIds[] = $item->getData($linkField); + $tierPrices[$item->getData($linkField)] = []; } if (!$productIds) { return $this; @@ -2103,57 +2104,27 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ $attribute = $this->getAttribute('tier_price'); + /* @var $backend \Magento\Catalog\Model\Product\Attribute\Backend\Tierprice */ + $backend = $attribute->getBackend(); $websiteId = 0; if (!$attribute->isScopeGlobal() && null !== $this->getStoreId()) { $websiteId = $this->_storeManager->getStore($this->getStoreId())->getWebsiteId(); } - $linkField = $this->getConnection()->getAutoIncrementField($this->getTable('catalog_product_entity')); - $connection = $this->getConnection(); - $columns = [ - 'price_id' => 'value_id', - 'website_id' => 'website_id', - 'all_groups' => 'all_groups', - 'cust_group' => 'customer_group_id', - 'price_qty' => 'qty', - 'price' => 'value', - 'product_id' => $linkField, - ]; - $select = $connection->select()->from( - $this->getTable('catalog_product_entity_tier_price'), - $columns - )->where( + $select = $backend->getResource()->getSelect($websiteId); + $select->columns(['product_id' => $linkField])->where( $linkField .' IN(?)', $productIds )->order( - [$linkField, 'qty'] + $linkField ); - if ($websiteId == 0) { - $select->where('website_id = ?', $websiteId); - } else { - $select->where('website_id IN(?)', [0, $websiteId]); - } - - foreach ($connection->fetchAll($select) as $row) { - $tierPrices[$row['product_id']][] = [ - 'website_id' => $row['website_id'], - 'cust_group' => $row['all_groups'] ? $this->_groupManagement->getAllCustomersGroup()->getId() : $row['cust_group'], - 'price_qty' => $row['price_qty'], - 'price' => $row['price'], - 'website_price' => $row['price'], - ]; + foreach ($this->getConnection()->fetchAll($select) as $row) { + $tierPrices[$row['product_id']][] = $row; } - /* @var $backend \Magento\Catalog\Model\Product\Attribute\Backend\Tierprice */ - $backend = $attribute->getBackend(); - foreach ($this->getItems() as $item) { - $data = $tierPrices[$item->getId()]; - if (!empty($data) && $websiteId) { - $data = $backend->preparePriceData($data, $item->getTypeId(), $websiteId); - } - $item->setData('tier_price', $data); + $backend->setPriceData($item, $tierPrices[$item->getData($linkField)]); } $this->setFlag('tier_price_added', true); diff --git a/app/code/Magento/Catalog/Setup/UpgradeSchema.php b/app/code/Magento/Catalog/Setup/UpgradeSchema.php index 03f3ac9581729b96eeab485da5be125072f23773..9683632f121b6af18a3397e53bac2943521683c9 100644 --- a/app/code/Magento/Catalog/Setup/UpgradeSchema.php +++ b/app/code/Magento/Catalog/Setup/UpgradeSchema.php @@ -32,6 +32,10 @@ class UpgradeSchema implements UpgradeSchemaInterface if (version_compare($context->getVersion(), '2.0.6', '<')) { $this->addUniqueKeyToCategoryProductTable($setup); } + + if (version_compare($context->getVersion(), '2.1.0', '<')) { + $this->addPercentageValueColumn($setup); + } $setup->endSetup(); } @@ -278,4 +282,25 @@ class UpgradeSchema implements UpgradeSchemaInterface $connection->dropColumn($setup->getTable($filedInfo['table']), $filedInfo['column']); } } + + /** + * Add percentage value column + * @param SchemaSetupInterface $setup + * @return void + */ + private function addPercentageValueColumn(SchemaSetupInterface $setup) + { + $connection = $setup->getConnection(); + $connection->addColumn( + $setup->getTable('catalog_product_entity_tier_price'), + 'percentage_value', + [ + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_DECIMAL, + 'nullable' => true, + 'length' => '5,2', + 'comment' => 'Percentage value', + 'after' => 'value' + ] + ); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/PriceTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/PriceTest.php index c51f9486c2ecb26c8fefb82ad3875c24b904af2d..5868e749446e391a987928314d3c133c1fc19537 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/PriceTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/PriceTest.php @@ -8,11 +8,15 @@ namespace Magento\Catalog\Test\Unit\Model\Product\Type; +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; +use Magento\Catalog\Api\Data\ProductTierPriceExtensionInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Customer\Model\GroupManagement; /** * Price Test + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PriceTest extends \PHPUnit_Framework_TestCase { @@ -60,18 +64,18 @@ class PriceTest extends \PHPUnit_Framework_TestCase $this->objectManagerHelper = new ObjectManagerHelper($this); $this->product = $this->objectManagerHelper->getObject(\Magento\Catalog\Model\Product::class); - $this->tpFactory = $this->getMockForAbstractClass( + $this->tpFactory = $this->getMock( \Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory::class, + ['create'], [], '', false, - true, - true, - ['create'] + false, + false ); $this->websiteMock = $this->getMock(\Magento\Store\Model\Website::class, ['getId'], [], '', false); - $storeMangerMock = $this->getMockForAbstractClass( + $storeMangerMock = $this->getMockForAbstractClass( \Magento\Store\Model\StoreManagerInterface::class, [], '', @@ -84,7 +88,7 @@ class PriceTest extends \PHPUnit_Framework_TestCase ->method('getWebsite') ->will($this->returnValue($this->websiteMock)); - $this->scopeConfigMock = $this->getMockForAbstractClass( + $this->scopeConfigMock = $this->getMockForAbstractClass( \Magento\Framework\App\Config\ScopeConfigInterface::class, [], '', @@ -94,7 +98,7 @@ class PriceTest extends \PHPUnit_Framework_TestCase ['getValue'] ); - $group = $this->getMock( + $group = $this->getMock( \Magento\Customer\Model\Data\Group::class, [], [], @@ -107,7 +111,7 @@ class PriceTest extends \PHPUnit_Framework_TestCase $this->groupManagementMock->expects($this->any())->method('getAllCustomersGroup') ->will($this->returnValue($group)); - $this->model = $this->objectManagerHelper->getObject( + $this->model = $this->objectManagerHelper->getObject( \Magento\Catalog\Model\Product\Type\Price::class, [ 'tierPriceFactory' => $this->tpFactory, @@ -164,12 +168,8 @@ class PriceTest extends \PHPUnit_Framework_TestCase public function testTierPrices($priceScope, $expectedWebsiteId) { // establish the behavior of the mocks - $this->scopeConfigMock->expects($this->any()) - ->method('getValue') - ->will($this->returnValue($priceScope)); - $this->websiteMock->expects($this->any()) - ->method('getId') - ->will($this->returnValue($expectedWebsiteId)); + $this->scopeConfigMock->expects($this->any())->method('getValue')->will($this->returnValue($priceScope)); + $this->websiteMock->expects($this->any())->method('getId')->will($this->returnValue($expectedWebsiteId)); $this->tpFactory->expects($this->any()) ->method('create') ->will($this->returnCallback(function () { @@ -177,14 +177,21 @@ class PriceTest extends \PHPUnit_Framework_TestCase })); // create sample TierPrice objects that would be coming from a REST call + $tierPriceExtensionMock = $this->getMockBuilder(ProductTierPriceExtensionInterface::class) + ->setMethods(['getWebsiteId', 'setWebsiteId', 'getPercentageValue', 'setPercentageValue']) + ->getMock(); + $tierPriceExtensionMock->expects($this->any())->method('getWebsiteId')->willReturn($expectedWebsiteId); + $tierPriceExtensionMock->expects($this->any())->method('getPercentageValue')->willReturn(null); $tp1 = $this->objectManagerHelper->getObject(\Magento\Catalog\Model\Product\TierPrice::class); $tp1->setValue(10); $tp1->setCustomerGroupId(1); $tp1->setQty(11); + $tp1->setExtensionAttributes($tierPriceExtensionMock); $tp2 = $this->objectManagerHelper->getObject(\Magento\Catalog\Model\Product\TierPrice::class); $tp2->setValue(20); $tp2->setCustomerGroupId(2); $tp2->setQty(22); + $tp2->setExtensionAttributes($tierPriceExtensionMock); $tps = [$tp1, $tp2]; // force the product to have null tier prices @@ -213,11 +220,28 @@ class PriceTest extends \PHPUnit_Framework_TestCase $this->assertEquals($tps[$i]->getQty(), $tpData['price_qty'], 'Qty does not match'); } + $tierPriceExtention = $this->getMockBuilder(ProductTierPriceExtensionInterface::class) + ->setMethods(['getWebsiteId', 'setWebsiteId', 'getPercentageValue', 'setPercentageValue']) + ->getMock(); + $tierPriceExtention->expects($this->any())->method('getPercentageValue')->willReturn(50); + $tierPriceExtention->expects($this->any())->method('setWebsiteId'); + $factoryMock = $this->getMockBuilder(ProductTierPriceExtensionFactory::class)->setMethods(['create']) + ->disableOriginalConstructor()->getMock(); + $factoryMock->expects($this->any())->method('create')->willReturn($tierPriceExtention); + + $reflection = new \ReflectionClass(get_class($this->model)); + $reflectionProperty = $reflection->getProperty('tierPriceExtensionFactory'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->model, $factoryMock); + // test with the data retrieved as a REST object $tpRests = $this->model->getTierPrices($this->product); $this->assertNotNull($tpRests); $this->assertTrue(is_array($tpRests)); $this->assertEquals(sizeof($tps), sizeof($tpRests)); + foreach ($tpRests as $tpRest) { + $this->assertEquals(50, $tpRest->getExtensionAttributes()->getPercentageValue()); + } for ($i = 0; $i < sizeof($tps); $i++) { $this->assertEquals( diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php index 3b92e54355fb66be4eb76cec2651ffbbab88434b..c42efd5a1e61fe1249c0cf28d53b8737d22f826d 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php @@ -7,9 +7,10 @@ namespace Magento\Catalog\Ui\DataProvider\Product\Form\Modifier; use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Catalog\Model\Locator\LocatorInterface; +use Magento\Customer\Model\Customer\Source\GroupSourceInterface; use Magento\Directory\Helper\Data; +use Magento\Framework\App\ObjectManager; use Magento\Store\Model\StoreManagerInterface; -use Magento\Customer\Api\Data\GroupInterface; use Magento\Customer\Api\GroupManagementInterface; use Magento\Customer\Api\GroupRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; @@ -81,6 +82,11 @@ class AdvancedPricing extends AbstractModifier */ protected $meta = []; + /** + * @var GroupSourceInterface + */ + private $customerGroupSource; + /** * @param LocatorInterface $locator * @param StoreManagerInterface $storeManager @@ -91,6 +97,7 @@ class AdvancedPricing extends AbstractModifier * @param Data $directoryHelper * @param ArrayManager $arrayManager * @param string $scopeName + * @param GroupSourceInterface $customerGroupSource * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -102,7 +109,8 @@ class AdvancedPricing extends AbstractModifier ModuleManager $moduleManager, Data $directoryHelper, ArrayManager $arrayManager, - $scopeName = '' + $scopeName = '', + GroupSourceInterface $customerGroupSource = null ) { $this->locator = $locator; $this->storeManager = $storeManager; @@ -113,6 +121,8 @@ class AdvancedPricing extends AbstractModifier $this->directoryHelper = $directoryHelper; $this->arrayManager = $arrayManager; $this->scopeName = $scopeName; + $this->customerGroupSource = $customerGroupSource + ?: ObjectManager::getInstance()->get(GroupSourceInterface::class); } /** @@ -174,7 +184,7 @@ class AdvancedPricing extends AbstractModifier * * @return $this */ - protected function customizeTierPrice() + private function customizeTierPrice() { $tierPricePath = $this->arrayManager->findPath( ProductAttributeInterface::CODE_TIER_PRICE, @@ -209,28 +219,13 @@ class AdvancedPricing extends AbstractModifier * * @return array */ - protected function getCustomerGroups() + private function getCustomerGroups() { if (!$this->moduleManager->isEnabled('Magento_Customer')) { return []; } - $customerGroups = [ - [ - 'label' => __('ALL GROUPS'), - 'value' => GroupInterface::CUST_GROUP_ALL, - ] - ]; - - /** @var GroupInterface[] $groups */ - $groups = $this->groupRepository->getList($this->searchCriteriaBuilder->create()); - foreach ($groups->getItems() as $group) { - $customerGroups[] = [ - 'label' => $group->getCode(), - 'value' => $group->getId(), - ]; - } - return $customerGroups; + return $this->customerGroupSource->toOptionArray(); } /** @@ -238,7 +233,7 @@ class AdvancedPricing extends AbstractModifier * * @return bool */ - protected function isScopeGlobal() + private function isScopeGlobal() { return $this->locator->getProduct() ->getResource() @@ -251,7 +246,7 @@ class AdvancedPricing extends AbstractModifier * * @return array */ - protected function getWebsites() + private function getWebsites() { $websites = [ [ @@ -292,7 +287,7 @@ class AdvancedPricing extends AbstractModifier * * @return int */ - protected function getDefaultCustomerGroup() + private function getDefaultCustomerGroup() { return $this->groupManagement->getAllCustomersGroup()->getId(); } @@ -316,7 +311,7 @@ class AdvancedPricing extends AbstractModifier * * @return bool */ - protected function isShowWebsiteColumn() + private function isShowWebsiteColumn() { if ($this->isScopeGlobal() || $this->storeManager->isSingleStoreMode()) { return false; @@ -329,7 +324,7 @@ class AdvancedPricing extends AbstractModifier * * @return bool */ - protected function isMultiWebsites() + private function isMultiWebsites() { return !$this->storeManager->isSingleStoreMode(); } @@ -339,7 +334,7 @@ class AdvancedPricing extends AbstractModifier * * @return bool */ - protected function isAllowChangeWebsite() + private function isAllowChangeWebsite() { if (!$this->isShowWebsiteColumn() || $this->locator->getProduct()->getStoreId()) { return false; @@ -352,7 +347,7 @@ class AdvancedPricing extends AbstractModifier * * @return $this */ - protected function addAdvancedPriceLink() + private function addAdvancedPriceLink() { $pricePath = $this->arrayManager->findPath( ProductAttributeInterface::CODE_PRICE, @@ -405,7 +400,7 @@ class AdvancedPricing extends AbstractModifier * @return array * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - protected function getTierPriceStructure($tierPricePath) + private function getTierPriceStructure($tierPricePath) { return [ 'arguments' => [ @@ -452,6 +447,7 @@ class AdvancedPricing extends AbstractModifier 'value' => $this->getDefaultWebsite(), 'visible' => $this->isMultiWebsites(), 'disabled' => ($this->isShowWebsiteColumn() && !$this->isAllowChangeWebsite()), + 'sortOrder' => 10, ], ], ], @@ -467,6 +463,7 @@ class AdvancedPricing extends AbstractModifier 'label' => __('Customer Group'), 'options' => $this->getCustomerGroups(), 'value' => $this->getDefaultCustomerGroup(), + 'sortOrder' => 20, ], ], ], @@ -480,6 +477,7 @@ class AdvancedPricing extends AbstractModifier 'dataType' => Number::NAME, 'label' => __('Quantity'), 'dataScope' => 'price_qty', + 'sortOrder' => 30, 'validation' => [ 'required-entry' => true, 'validate-greater-than-zero' => true, @@ -494,6 +492,7 @@ class AdvancedPricing extends AbstractModifier 'data' => [ 'config' => [ 'componentType' => Field::NAME, + 'component' => 'Magento_Catalog/js/form/element/price-input', 'formElement' => Input::NAME, 'dataType' => Price::NAME, 'label' => __('Price'), @@ -502,11 +501,15 @@ class AdvancedPricing extends AbstractModifier 'addbefore' => $this->locator->getStore() ->getBaseCurrency() ->getCurrencySymbol(), + 'sortOrder' => 40, 'validation' => [ 'required-entry' => true, 'validate-greater-than-zero' => true, 'validate-number' => true, ], + 'imports' => [ + 'priceValue' => '${ $.provider }:data.product.price', + ], ], ], ], @@ -518,6 +521,7 @@ class AdvancedPricing extends AbstractModifier 'componentType' => 'actionDelete', 'dataType' => Text::NAME, 'label' => '', + 'sortOrder' => 50, ], ], ], @@ -533,7 +537,7 @@ class AdvancedPricing extends AbstractModifier * * @return $this */ - protected function specialPriceDataToInline() + private function specialPriceDataToInline() { $pathFrom = $this->arrayManager->findPath('special_from_date', $this->meta, null, 'children'); $pathTo = $this->arrayManager->findPath('special_to_date', $this->meta, null, 'children'); @@ -589,7 +593,7 @@ class AdvancedPricing extends AbstractModifier * * @return $this */ - protected function customizeAdvancedPricing() + private function customizeAdvancedPricing() { $this->meta['advanced-pricing']['arguments']['data']['config']['opened'] = true; $this->meta['advanced-pricing']['arguments']['data']['config']['collapsible'] = false; @@ -646,9 +650,9 @@ class AdvancedPricing extends AbstractModifier /** * Retrieve store * - * @return \Magento\Store\Model\Store + * @return \Magento\Store\Api\Data\StoreInterface */ - protected function getStore() + private function getStore() { return $this->locator->getStore(); } diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 36f072d3b0a15b358463a2715e5e214c30b51b24..49ad7d67d705899077d4beb13f4a26383982dcf5 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -46,6 +46,7 @@ <preference for="Magento\Catalog\Api\AttributeSetFinderInterface" type="Magento\Catalog\Model\Product\Attribute\AttributeSetFinder" /> <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"/> <type name="Magento\Customer\Model\ResourceModel\Visitor"> <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" /> </type> @@ -470,6 +471,7 @@ <preference for="Magento\Catalog\Api\ProductCustomOptionRepositoryInterface" type="\Magento\Catalog\Model\Product\Option\Repository" /> <preference for="Magento\Catalog\Api\Data\ProductCustomOptionTypeInterface" type="Magento\Catalog\Model\Product\Option\Type" /> <preference for="Magento\Catalog\Api\ProductTierPriceManagementInterface" type="\Magento\Catalog\Model\Product\TierPriceManagement" /> + <preference for="Magento\Catalog\Api\ScopedProductTierPriceManagementInterface" type="\Magento\Catalog\Model\Product\ScopedTierPriceManagement" /> <preference for="Magento\Catalog\Api\Data\ProductTierPriceInterface" type="Magento\Catalog\Model\Product\TierPrice" /> <preference for="Magento\Catalog\Api\Data\CategoryProductLinkInterface" type="Magento\Catalog\Model\CategoryProductLink" /> <preference for="Magento\Catalog\Api\ProductCustomOptionTypeListInterface" type="Magento\Catalog\Model\ProductOptions\TypeList" /> diff --git a/app/code/Magento/Catalog/etc/extension_attributes.xml b/app/code/Magento/Catalog/etc/extension_attributes.xml index 976031cb937fe414bb97eff0560e1496ab3ac691..509c3240bb6c8046ae74fcc36a55160376e195f1 100644 --- a/app/code/Magento/Catalog/etc/extension_attributes.xml +++ b/app/code/Magento/Catalog/etc/extension_attributes.xml @@ -25,4 +25,10 @@ <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface"> <attribute code="category_links" type="Magento\Catalog\Api\Data\CategoryLinkInterface[]" /> </extension_attributes> + <extension_attributes for="Magento\Catalog\Api\Data\ProductTierPriceInterface"> + <attribute code="percentage_value" type="float" /> + </extension_attributes> + <extension_attributes for="Magento\Catalog\Api\Data\ProductTierPriceInterface"> + <attribute code="website_id" type="int" /> + </extension_attributes> </config> diff --git a/app/code/Magento/Catalog/etc/module.xml b/app/code/Magento/Catalog/etc/module.xml index 87e82543fc65b2991d5f10da91941f389dc18fcd..1250b55b968480bf9c1d9f9952da6d864048d98f 100644 --- a/app/code/Magento/Catalog/etc/module.xml +++ b/app/code/Magento/Catalog/etc/module.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Catalog" setup_version="2.0.7"> + <module name="Magento_Catalog" setup_version="2.1.0"> <sequence> <module name="Magento_Eav"/> <module name="Magento_Cms"/> diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/form/element/price-input.js b/app/code/Magento/Catalog/view/adminhtml/web/js/form/element/price-input.js new file mode 100644 index 0000000000000000000000000000000000000000..0939619809a460b49cc7f254702797357c3b06f5 --- /dev/null +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/form/element/price-input.js @@ -0,0 +1,15 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +define([ + 'Magento_Ui/js/form/element/abstract' +], function (Abstract) { + 'use strict'; + + return Abstract.extend({ + defaults: { + elementTmpl: 'Magento_Catalog/form/element/price-input' + } + }); +}); diff --git a/app/code/Magento/Catalog/view/adminhtml/web/template/form/element/price-input.html b/app/code/Magento/Catalog/view/adminhtml/web/template/form/element/price-input.html new file mode 100644 index 0000000000000000000000000000000000000000..ce8ae751e6702ff11099db5a06c8dbb8d4b1cbb9 --- /dev/null +++ b/app/code/Magento/Catalog/view/adminhtml/web/template/form/element/price-input.html @@ -0,0 +1,22 @@ +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<input class="admin__control-text" type="text" + data-bind=" + event: { + change: userChanges, + input: onInput + }, + value: value, + hasFocus: focused, + valueUpdate: valueUpdate, + attr: { + name: inputName, + placeholder: placeholder, + 'aria-describedby': noticeId, + id: uid, + disabled: disabled + }"/> diff --git a/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml b/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml index 362aac7c8cf309523fedae6ea833e08f1bab789b..64e0d2ec59d105f2e312a1e24a5b5ab864b8eec7 100644 --- a/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml +++ b/app/code/Magento/CatalogInventory/view/adminhtml/ui_component/product_form.xml @@ -242,7 +242,7 @@ </argument> <field name="customer_group_id"> <argument name="data" xsi:type="array"> - <item name="options" xsi:type="object">Magento\Customer\Model\Customer\Source\Group</item> + <item name="options" xsi:type="object">Magento\Customer\Model\Customer\Source\GroupSourceInterface</item> <item name="config" xsi:type="array"> <item name="dataType" xsi:type="string">text</item> <item name="formElement" xsi:type="string">select</item> 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 3aff684574652c2c9de84a2a6713fa8d7002d82a..af60e0247169bb402d5b6d71be4576ad2ed24566 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\CatalogRule\Model\Rule\CustomerGroupsOptionsProvider</item> + <item name="options" xsi:type="object">\Magento\Customer\Model\Customer\Source\GroupSourceInterface</item> </argument> </field> <field name="from_date"> diff --git a/app/code/Magento/ConfigurableProduct/Setup/UpgradeData.php b/app/code/Magento/ConfigurableProduct/Setup/UpgradeData.php new file mode 100644 index 0000000000000000000000000000000000000000..4c321f521bb79491ab282545b13d29040cc382bd --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Setup/UpgradeData.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\ConfigurableProduct\Setup; + +use Magento\ConfigurableProduct\Model\Product\Type\Configurable; +use Magento\Framework\Setup\UpgradeDataInterface; +use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\Setup\ModuleDataSetupInterface; +use Magento\Eav\Setup\EavSetup; +use Magento\Eav\Setup\EavSetupFactory; + +/** + * Upgrade Data script + * @codeCoverageIgnore + */ +class UpgradeData implements UpgradeDataInterface +{ + /** + * EAV setup factory + * + * @var EavSetupFactory + */ + private $eavSetupFactory; + + /** + * Init + * + * @param EavSetupFactory $eavSetupFactory + */ + public function __construct(EavSetupFactory $eavSetupFactory) + { + $this->eavSetupFactory = $eavSetupFactory; + } + + /** + * {@inheritdoc} + */ + public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) + { + $setup->startSetup(); + if (version_compare($context->getVersion(), '2.2.0') < 0) { + /** @var EavSetup $eavSetup */ + $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]); + $relatedProductTypes = explode( + ',', + $eavSetup->getAttribute(\Magento\Catalog\Model\Product::ENTITY, 'tier_price', 'apply_to') + ); + $key = array_search(Configurable::TYPE_CODE, $relatedProductTypes); + if ($key !== false) { + unset($relatedProductTypes[$key]); + $eavSetup->updateAttribute( + \Magento\Catalog\Model\Product::ENTITY, + 'tier_price', + 'apply_to', + implode(',', $relatedProductTypes) + ); + } + } + + $setup->endSetup(); + } +} diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php index 21730cf65f020f5766fafe123b4448a1f387f6f9..efb409dd156af2af33f6a233a583fdf8a3fcedd0 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php @@ -384,7 +384,7 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase 'addAttributeToSelect', 'addFilterByRequiredOptions', 'setStoreId', - 'addPriceData', + 'addTierPriceData', 'getIterator', 'load', ] @@ -393,7 +393,7 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase $productCollection->expects($this->any())->method('addAttributeToSelect')->will($this->returnSelf()); $productCollection->expects($this->any())->method('setProductFilter')->will($this->returnSelf()); $productCollection->expects($this->any())->method('setFlag')->will($this->returnSelf()); - $productCollection->expects($this->any())->method('addPriceData')->will($this->returnSelf()); + $productCollection->expects($this->any())->method('addTierPriceData')->will($this->returnSelf()); $productCollection->expects($this->any())->method('addFilterByRequiredOptions')->will($this->returnSelf()); $productCollection->expects($this->any())->method('setStoreId')->with(5)->will($this->returnValue([])); $productCollection->expects($this->any())->method('getIterator')->willReturn( diff --git a/app/code/Magento/ConfigurableProduct/etc/module.xml b/app/code/Magento/ConfigurableProduct/etc/module.xml index 8a64e9f026d9d86fd22b5c4d8fb8bf40cb781c29..706338092c4b6ae56d25dd3c125a728578b6be19 100644 --- a/app/code/Magento/ConfigurableProduct/etc/module.xml +++ b/app/code/Magento/ConfigurableProduct/etc/module.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_ConfigurableProduct" setup_version="2.0.0"> + <module name="Magento_ConfigurableProduct" setup_version="2.2.0"> <sequence> <module name="Magento_Catalog"/> <module name="Magento_Msrp"/> diff --git a/app/code/Magento/Customer/Model/Config/Source/Group.php b/app/code/Magento/Customer/Model/Config/Source/Group.php index 40c7712f5aab3724071ef9def4ecd27eb5e1b8a5..e693bf7a3c45e29791dede512a6b00a293633cf7 100644 --- a/app/code/Magento/Customer/Model/Config/Source/Group.php +++ b/app/code/Magento/Customer/Model/Config/Source/Group.php @@ -6,6 +6,8 @@ namespace Magento\Customer\Model\Config\Source; use Magento\Customer\Api\GroupManagementInterface; +use Magento\Customer\Model\Customer\Attribute\Source\GroupSourceLoggedInOnlyInterface; +use Magento\Framework\App\ObjectManager; class Group implements \Magento\Framework\Option\ArrayInterface { @@ -15,37 +17,48 @@ class Group implements \Magento\Framework\Option\ArrayInterface protected $_options; /** + * @deprecated * @var GroupManagementInterface */ protected $_groupManagement; /** + * @deprecated * @var \Magento\Framework\Convert\DataObject */ protected $_converter; + /** + * @var GroupSourceLoggedInOnlyInterface + */ + private $groupSourceLoggedInOnly; + /** * @param GroupManagementInterface $groupManagement * @param \Magento\Framework\Convert\DataObject $converter + * @param GroupSourceLoggedInOnlyInterface $groupSourceForLoggedInCustomers */ public function __construct( GroupManagementInterface $groupManagement, - \Magento\Framework\Convert\DataObject $converter + \Magento\Framework\Convert\DataObject $converter, + GroupSourceLoggedInOnlyInterface $groupSourceForLoggedInCustomers = null ) { $this->_groupManagement = $groupManagement; $this->_converter = $converter; + $this->groupSourceLoggedInOnly = $groupSourceForLoggedInCustomers + ?: ObjectManager::getInstance()->get(GroupSourceLoggedInOnlyInterface::class); } /** - * @return array + * @inheritdoc */ public function toOptionArray() { if (!$this->_options) { - $groups = $this->_groupManagement->getLoggedInGroups(); - $this->_options = $this->_converter->toOptionArray($groups, 'id', 'code'); + $this->_options = $this->groupSourceLoggedInOnly->toOptionArray(); array_unshift($this->_options, ['value' => '', 'label' => __('-- Please Select --')]); } + return $this->_options; } } diff --git a/app/code/Magento/Customer/Model/Config/Source/Group/Multiselect.php b/app/code/Magento/Customer/Model/Config/Source/Group/Multiselect.php index 708e3243e26992589f553ddf4e7eda6bc19ed02b..adfc78448cdfc1b29412a9e5e5caca3907c6e487 100644 --- a/app/code/Magento/Customer/Model/Config/Source/Group/Multiselect.php +++ b/app/code/Magento/Customer/Model/Config/Source/Group/Multiselect.php @@ -5,7 +5,9 @@ */ namespace Magento\Customer\Model\Config\Source\Group; +use Magento\Customer\Model\Customer\Attribute\Source\GroupSourceLoggedInOnlyInterface; use Magento\Customer\Api\GroupManagementInterface; +use Magento\Framework\App\ObjectManager; class Multiselect implements \Magento\Framework\Option\ArrayInterface { @@ -17,25 +19,36 @@ class Multiselect implements \Magento\Framework\Option\ArrayInterface protected $_options; /** + * @deprecated * @var GroupManagementInterface */ protected $_groupManagement; /** + * @deprecated * @var \Magento\Framework\Convert\DataObject */ protected $_converter; + /** + * @var GroupSourceLoggedInOnlyInterface + */ + private $groupSourceLoggedInOnly; + /** * @param GroupManagementInterface $groupManagement * @param \Magento\Framework\Convert\DataObject $converter + * @param GroupSourceLoggedInOnlyInterface|null $groupSourceLoggedInOnly */ public function __construct( GroupManagementInterface $groupManagement, - \Magento\Framework\Convert\DataObject $converter + \Magento\Framework\Convert\DataObject $converter, + GroupSourceLoggedInOnlyInterface $groupSourceLoggedInOnly = null ) { $this->_groupManagement = $groupManagement; $this->_converter = $converter; + $this->groupSourceLoggedInOnly = $groupSourceLoggedInOnly + ?: ObjectManager::getInstance()->get(GroupSourceLoggedInOnlyInterface::class); } /** @@ -46,8 +59,7 @@ class Multiselect implements \Magento\Framework\Option\ArrayInterface public function toOptionArray() { if (!$this->_options) { - $groups = $this->_groupManagement->getLoggedInGroups(); - $this->_options = $this->_converter->toOptionArray($groups, 'id', 'code'); + $this->_options = $this->groupSourceLoggedInOnly->toOptionArray(); } return $this->_options; } diff --git a/app/code/Magento/Customer/Model/Customer/Attribute/Source/Group.php b/app/code/Magento/Customer/Model/Customer/Attribute/Source/Group.php index 4663d26b628b09d13a9e2a1556f03a31b951a181..9af4b7fc67340b390aec50dc4fb0c2891aef03e3 100644 --- a/app/code/Magento/Customer/Model/Customer/Attribute/Source/Group.php +++ b/app/code/Magento/Customer/Model/Customer/Attribute/Source/Group.php @@ -12,7 +12,7 @@ use Magento\Customer\Api\GroupManagementInterface; * * @author Magento Core Team <core@magentocommerce.com> */ -class Group extends \Magento\Eav\Model\Entity\Attribute\Source\Table +class Group extends \Magento\Eav\Model\Entity\Attribute\Source\Table implements GroupSourceLoggedInOnlyInterface { /** * @var GroupManagementInterface @@ -50,6 +50,7 @@ class Group extends \Magento\Eav\Model\Entity\Attribute\Source\Table $groups = $this->_groupManagement->getLoggedInGroups(); $this->_options = $this->_converter->toOptionArray($groups, 'id', 'code'); } + return $this->_options; } } diff --git a/app/code/Magento/Customer/Model/Customer/Attribute/Source/GroupSourceLoggedInOnlyInterface.php b/app/code/Magento/Customer/Model/Customer/Attribute/Source/GroupSourceLoggedInOnlyInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..5570b4073dfdba163fb403c528790e26d904713c --- /dev/null +++ b/app/code/Magento/Customer/Model/Customer/Attribute/Source/GroupSourceLoggedInOnlyInterface.php @@ -0,0 +1,14 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Model\Customer\Attribute\Source; + +use \Magento\Framework\Data\OptionSourceInterface; + +interface GroupSourceLoggedInOnlyInterface extends OptionSourceInterface +{ + +} diff --git a/app/code/Magento/Customer/Model/Customer/Source/Group.php b/app/code/Magento/Customer/Model/Customer/Source/Group.php index 59dc3be52f1e57f87d7cd6b7888f27050f83e1dc..5973ec6ffaab3e91a706e4ec542500ec61b41b29 100644 --- a/app/code/Magento/Customer/Model/Customer/Source/Group.php +++ b/app/code/Magento/Customer/Model/Customer/Source/Group.php @@ -5,12 +5,13 @@ */ namespace Magento\Customer\Model\Customer\Source; +use Magento\Customer\Api\Data\GroupSearchResultsInterface; use Magento\Framework\Module\Manager as ModuleManager; use Magento\Customer\Api\Data\GroupInterface; use Magento\Customer\Api\GroupRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; -class Group implements \Magento\Framework\Option\ArrayInterface +class Group implements GroupSourceInterface { /** * @var ModuleManager @@ -52,14 +53,13 @@ class Group implements \Magento\Framework\Option\ArrayInterface if (!$this->moduleManager->isEnabled('Magento_Customer')) { return []; } - $customerGroups = [ - [ - 'label' => __('ALL GROUPS'), - 'value' => GroupInterface::CUST_GROUP_ALL, - ] + $customerGroups = []; + $customerGroups[] = [ + 'label' => __('ALL GROUPS'), + 'value' => GroupInterface::CUST_GROUP_ALL, ]; - /** @var GroupInterface[] $groups */ + /** @var GroupSearchResultsInterface $groups */ $groups = $this->groupRepository->getList($this->searchCriteriaBuilder->create()); foreach ($groups->getItems() as $group) { $customerGroups[] = [ diff --git a/app/code/Magento/Customer/Model/Customer/Source/GroupSourceInterface.php b/app/code/Magento/Customer/Model/Customer/Source/GroupSourceInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..c044740dc35e75ec46f5b20d66b79ad5832a31f9 --- /dev/null +++ b/app/code/Magento/Customer/Model/Customer/Source/GroupSourceInterface.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Customer\Model\Customer\Source; + +use Magento\Framework\Data\OptionSourceInterface; + +interface GroupSourceInterface extends OptionSourceInterface +{ + +} diff --git a/app/code/Magento/Customer/Test/Unit/Model/Config/Source/Group/MultiselectTest.php b/app/code/Magento/Customer/Test/Unit/Model/Config/Source/Group/MultiselectTest.php index 3b8bea594ed523e268e13f5297cd00db2fdae0c0..bc64351f3d01591cea94892b759e08cefeb7d81d 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Config/Source/Group/MultiselectTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Config/Source/Group/MultiselectTest.php @@ -6,6 +6,8 @@ */ namespace Magento\Customer\Test\Unit\Model\Config\Source\Group; +use Magento\Customer\Model\Customer\Attribute\Source\GroupSourceLoggedInOnlyInterface; + class MultiselectTest extends \PHPUnit_Framework_TestCase { /** @@ -23,22 +25,29 @@ class MultiselectTest extends \PHPUnit_Framework_TestCase */ protected $converterMock; + /** + * @var GroupSourceLoggedInOnlyInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $groupSourceLoggedInOnly; + protected function setUp() { $this->groupServiceMock = $this->getMock(\Magento\Customer\Api\GroupManagementInterface::class); $this->converterMock = $this->getMock(\Magento\Framework\Convert\DataObject::class, [], [], '', false); - $this->model = - new \Magento\Customer\Model\Config\Source\Group\Multiselect($this->groupServiceMock, $this->converterMock); + $this->groupSourceLoggedInOnly = $this->getMockBuilder(GroupSourceLoggedInOnlyInterface::class)->getMock(); + $this->model = new \Magento\Customer\Model\Config\Source\Group\Multiselect( + $this->groupServiceMock, + $this->converterMock, + $this->groupSourceLoggedInOnly + ); } public function testToOptionArray() { $expectedValue = ['General', 'Retail']; - $this->groupServiceMock->expects($this->once()) - ->method('getLoggedInGroups') - ->will($this->returnValue($expectedValue)); - $this->converterMock->expects($this->once())->method('toOptionArray') - ->with($expectedValue, 'id', 'code')->will($this->returnValue($expectedValue)); + $this->groupServiceMock->expects($this->never())->method('getLoggedInGroups'); + $this->converterMock->expects($this->never())->method('toOptionArray'); + $this->groupSourceLoggedInOnly->expects($this->once())->method('toOptionArray')->willReturn($expectedValue); $this->assertEquals($expectedValue, $this->model->toOptionArray()); } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/Config/Source/GroupTest.php b/app/code/Magento/Customer/Test/Unit/Model/Config/Source/GroupTest.php index edb3405451ecab918ca046f91db5a7ce1dcc037d..55b495f9c419b4d2a208f2209f9c7049564fa367 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Config/Source/GroupTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Config/Source/GroupTest.php @@ -6,10 +6,21 @@ */ namespace Magento\Customer\Test\Unit\Model\Config\Source; +use Magento\Customer\Api\GroupManagementInterface; +use Magento\Customer\Model\Config\Source\Group; +use Magento\Customer\Model\Customer\Attribute\Source\GroupSourceLoggedInOnlyInterface; +use Magento\Framework\Convert\DataObject; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + class GroupTest extends \PHPUnit_Framework_TestCase { /** - * @var \Magento\Customer\Model\Config\Source\Group + * @var GroupSourceLoggedInOnlyInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $groupSource; + + /** + * @var Group */ protected $model; @@ -25,20 +36,30 @@ class GroupTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $this->groupServiceMock = $this->getMock(\Magento\Customer\Api\GroupManagementInterface::class); - $this->converterMock = $this->getMock(\Magento\Framework\Convert\DataObject::class, [], [], '', false); - $this->model = - new \Magento\Customer\Model\Config\Source\Group($this->groupServiceMock, $this->converterMock); + $this->groupServiceMock = $this->getMock(GroupManagementInterface::class); + $this->converterMock = $this->getMock(DataObject::class, [], [], '', false); + $this->groupSource = $this->getMockBuilder(GroupSourceLoggedInOnlyInterface::class) + ->getMockForAbstractClass(); + $this->model = (new ObjectManager($this))->getObject( + Group::class, + [ + 'groupManagement' => $this->groupServiceMock, + 'converter' => $this->converterMock, + 'groupSourceForLoggedInCustomers' => $this->groupSource, + ] + ); } public function testToOptionArray() { $expectedValue = ['General', 'Retail']; - $this->groupServiceMock->expects($this->once()) - ->method('getLoggedInGroups') - ->will($this->returnValue($expectedValue)); - $this->converterMock->expects($this->once())->method('toOptionArray') - ->with($expectedValue, 'id', 'code')->will($this->returnValue($expectedValue)); + $this->groupServiceMock->expects($this->never())->method('getLoggedInGroups'); + $this->converterMock->expects($this->never())->method('toOptionArray'); + + $this->groupSource->expects($this->once()) + ->method('toOptionArray') + ->willReturn($expectedValue); + array_unshift($expectedValue, ['value' => '', 'label' => __('-- Please Select --')]); $this->assertEquals($expectedValue, $this->model->toOptionArray()); } diff --git a/app/code/Magento/Customer/etc/di.xml b/app/code/Magento/Customer/etc/di.xml index d8f320161ab060b9cce690848fb00a402f030bb4..ebde958f75bc1f678aafd7ccd677c1536f76ef69 100644 --- a/app/code/Magento/Customer/etc/di.xml +++ b/app/code/Magento/Customer/etc/di.xml @@ -51,6 +51,10 @@ type="Magento\Customer\Helper\View" /> <preference for="Magento\Customer\Model\Address\CustomAttributeListInterface" type="Magento\Customer\Model\Address\CustomAttributeList" /> + <preference for="Magento\Customer\Model\Customer\Source\GroupSourceInterface" + type="Magento\Customer\Model\Customer\Source\Group" /> + <preference for="Magento\Customer\Model\Customer\Attribute\Source\GroupSourceLoggedInOnlyInterface" + type="Magento\Customer\Model\Customer\Attribute\Source\Group"/> <type name="Magento\Customer\Model\Session"> <arguments> <argument name="configShare" xsi:type="object">Magento\Customer\Model\Config\Share\Proxy</argument> diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php index 6287af52bb8896cbdb0dbf38a4756493a26cde8f..45d120db4755bb7c461cd575473810baf0077198 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/AbstractForm.php @@ -194,12 +194,17 @@ abstract class AbstractForm extends \Magento\Sales\Block\Adminhtml\Order\Create\ if ($inputType == 'select' || $inputType == 'multiselect') { $options = []; foreach ($attribute->getOptions() as $optionData) { - $options[] = ConvertArray::toFlatArray( - $this->dataObjectProcessor->buildOutputDataArray( - $optionData, - \Magento\Customer\Api\Data\OptionInterface::class - ) + $data = $this->dataObjectProcessor->buildOutputDataArray( + $optionData, + \Magento\Customer\Api\Data\OptionInterface::class ); + foreach ($data as $key => $value) { + if (is_array($value)) { + unset($data[$key]); + $data['value'] = $value; + } + } + $options[] = $data; } $element->setValues($options); } elseif ($inputType == 'date') { diff --git a/app/code/Magento/Ui/view/base/web/js/form/components/insert-form.js b/app/code/Magento/Ui/view/base/web/js/form/components/insert-form.js index f68e322a49dea5256093efded3fd9feaf827461d..11643d075a7965641617abc33440116bb4fd6fa1 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/components/insert-form.js +++ b/app/code/Magento/Ui/view/base/web/js/form/components/insert-form.js @@ -38,7 +38,10 @@ define([ el.innerHTML = elem; actions = el.getElementsByClassName(actionsClass)[0]; - el.removeChild(actions); + + if (actions) { + el.removeChild(actions); + } return el.innerHTML; } diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductTierPriceManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductTierPriceManagementTest.php index b1a1e7a36858db51016f7282e5bad9ddde2e9259..ff7968292874eaf63f7c3970c11b3216868606a1 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductTierPriceManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductTierPriceManagementTest.php @@ -51,7 +51,7 @@ class ProductTierPriceManagementTest extends WebapiAbstract public function getListDataProvider() { return [ - [0, 1, 5, 3], + [0, 2, 5, 3], [1, 0, null, null], ['all', 2, 8, 2], ]; diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Webapi.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Webapi.php index f3d839ecf8a8e5e9f71d5617c2e62f2b90559dd8..4ec372ab7163bb627bdf1353618f81231bed504d 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Webapi.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Handler/CatalogProductSimple/Webapi.php @@ -347,8 +347,16 @@ class Webapi extends AbstractWebApi implements CatalogProductSimpleInterface $priceInfo['customer_group_id'] = $priceInfo['cust_group']; unset($priceInfo['cust_group']); - $priceInfo['value'] = $priceInfo['price']; - unset($priceInfo['price']); + if (isset($priceInfo['price'])) { + $priceInfo['value'] = $priceInfo['price']; + unset($priceInfo['price']); + } + unset($priceInfo['value_type']); + + if (isset($priceInfo['percentage_value'])) { + $priceInfo['extension_attributes']['percentage_value'] = $priceInfo['percentage_value']; + unset($priceInfo['percentage_value']); + } $priceInfo['qty'] = $priceInfo['price_qty']; unset($priceInfo['price_qty']); diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Grid.php b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Grid.php index bd7d10a7cf04e5f4a1995edad8cbaec1d7d28fa1..3c2a9ed9c9b0fd2b09ea9435ce8cb0b43b46a58e 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Grid.php +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Block/Adminhtml/Order/Grid.php @@ -96,7 +96,7 @@ class Grid extends DataGrid $this->openFilterBlock(); $storeGroupElements = $this->_rootElement->find($this->filters['purchase_point']['selector']) - ->getElements('//option/preceding-sibling::optgroup[1]', Locator::SELECTOR_XPATH); + ->getElements('.//option/preceding-sibling::optgroup[1]', Locator::SELECTOR_XPATH); $result = []; foreach ($storeGroupElements as $storeGroupElement) { diff --git a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricingTest.php b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricingTest.php index e32939b195b7dfde6a9dd3a09c55dc14af2efed2..e5fd4cc0d9630080eb7c06f2558b02be889f8a57 100644 --- a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricingTest.php +++ b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricingTest.php @@ -124,7 +124,11 @@ class AdvancedPricingTest extends \PHPUnit_Framework_TestCase $this->assertEquals(3, count($tierPriceCollection)); /** @var \Magento\Catalog\Model\Product\TierPrice $tierPrice */ foreach ($tierPriceCollection as $tierPrice) { - $this->assertContains($tierPrice->getData(), $this->expectedTierPrice[$sku]); + $this->assertEquals(0, $tierPrice->getExtensionAttributes()->getPercentageValue()); + $this->assertEquals(0, $tierPrice->getExtensionAttributes()->getWebsiteId()); + $tierPriceData = $tierPrice->getData(); + unset($tierPriceData['extension_attributes']); + $this->assertContains($tierPriceData, $this->expectedTierPrice[$sku]); } } } @@ -240,7 +244,11 @@ class AdvancedPricingTest extends \PHPUnit_Framework_TestCase $this->assertEquals(3, count($tierPriceCollection)); /** @var \Magento\Catalog\Model\Product\TierPrice $tierPrice */ foreach ($tierPriceCollection as $tierPrice) { - $this->assertContains($tierPrice->getData(), $this->expectedTierPrice[$sku]); + $this->assertEquals(0, $tierPrice->getExtensionAttributes()->getPercentageValue()); + $this->assertEquals(0, $tierPrice->getExtensionAttributes()->getWebsiteId()); + $tierPriceData = $tierPrice->getData(); + unset($tierPriceData['extension_attributes']); + $this->assertContains($tierPriceData, $this->expectedTierPrice[$sku]); } } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/TierpriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/TierpriceTest.php index c69bdc1738bd471b14ac31a3ddeb432b632ed441..89dd932f8a023c4adb2c7cc17cc04ad2eddfd6c6 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/TierpriceTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/TierpriceTest.php @@ -95,6 +95,21 @@ class TierpriceTest extends \PHPUnit_Framework_TestCase $this->_model->validate($product); } + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testValidatePercentage() + { + $product = new \Magento\Framework\DataObject(); + $product->setTierPrice( + [ + ['website_id' => 0, 'cust_group' => 1, 'price_qty' => 2, 'percentage_value' => 101], + ] + ); + + $this->_model->validate($product); + } + public function testPreparePriceData() { $data = [ @@ -122,7 +137,7 @@ class TierpriceTest extends \PHPUnit_Framework_TestCase $this->_model->afterLoad($product); $price = $product->getTierPrice(); $this->assertNotEmpty($price); - $this->assertEquals(3, count($price)); + $this->assertEquals(4, count($price)); } /** diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php index f0a665d8be8b7eea90480af5fad416b2659337d3..7f13f6c9c52962d5876f62f2182205e825e6aa20 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php @@ -17,6 +17,11 @@ class CollectionTest extends \PHPUnit_Framework_TestCase */ protected $processor; + /** + * @var \Magento\Catalog\Api\ProductRepositoryInterface + */ + private $productRepository; + /** * Sets up the fixture, for example, opens a network connection. * This method is called before a test is executed. @@ -30,6 +35,10 @@ class CollectionTest extends \PHPUnit_Framework_TestCase $this->processor = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\Catalog\Model\Indexer\Product\Price\Processor::class ); + + $this->productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Api\ProductRepositoryInterface::class + ); } /** @@ -100,4 +109,19 @@ class CollectionTest extends \PHPUnit_Framework_TestCase $this->assertCount(2, $items); $this->assertEquals(15, $product->getPrice()); } + + /** + * @magentoDataFixture Magento/Catalog/Model/ResourceModel/_files/product_simple.php + * @magentoDbIsolation enabled + */ + public function testGetProductsWithTierPrice() + { + $product = $this->productRepository->get('simple products'); + $items = $this->collection->addIdFilter($product->getId())->addAttributeToSelect('price') + ->load()->addTierPriceData(); + $tierPrices = $items->getFirstItem()->getTierPrices(); + $this->assertCount(3, $tierPrices); + $this->assertEquals(50, $tierPrices[2]->getExtensionAttributes()->getPercentageValue()); + $this->assertEquals(5, $tierPrices[2]->getValue()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/_files/product_simple.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/_files/product_simple.php index 60e5ad4f8b76a5789de5ca476a3f7cb453f35fa8..c1a7ef37dacc5c3bfc169af3b4f868371433c055 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/_files/product_simple.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/_files/product_simple.php @@ -10,7 +10,6 @@ $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->create(\Magento\Catalog\Model\Product::class); $product->isObjectNew(true); $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) - ->setId(2) ->setAttributeSetId(4) ->setWebsiteIds([1]) ->setName('Simple Products') @@ -33,6 +32,12 @@ $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) 'price_qty' => 21, 'price' => 81, ], + [ + 'website_id' => 0, + 'cust_group' => Group::CUST_GROUP_ALL, + 'price_qty' => 30, + 'percentage_value' => 50, + ], ] ) ->setDescription('Description with <b>html tag</b>') 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 794a2f9087a8ec7035f92e042008c35624b62653..aadf1e74a883ce823d574178cba29ca617f5abf0 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory; + \Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize(); /** @var \Magento\TestFramework\ObjectManager $objectManager */ @@ -12,6 +14,92 @@ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); /** @var \Magento\Catalog\Api\CategoryLinkManagementInterface $categoryLinkManagement */ $categoryLinkManagement = $objectManager->create(\Magento\Catalog\Api\CategoryLinkManagementInterface::class); +$tierPrices = []; +/** @var \Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory $tierPriceFactory */ +$tierPriceFactory = $objectManager->get(\Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory::class); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, + 'qty' => 2, + 'value' => 8 + ] + ] +); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, + 'qty' => 5, + 'value' => 5 + ] + ] +); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => \Magento\Customer\Model\Group::NOT_LOGGED_IN_ID, + 'qty' => 3, + 'value' => 5 + ] + ] +); +/** @var $tpExtensionAttributes */ +$tpExtensionAttributesFactory = $objectManager->create(ProductTierPriceExtensionFactory::class); +$tpExtensionAttributes = $tpExtensionAttributesFactory->create()->setPercentageValue(50); + +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => \Magento\Customer\Model\Group::NOT_LOGGED_IN_ID, + 'qty' => 10 + ] + ] +)->setExtensionAttributes($tpExtensionAttributes); + +$tierPrices = []; +/** @var \Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory $tierPriceFactory */ +$tierPriceFactory = $objectManager->get(\Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory::class); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, + 'qty' => 2, + 'value' => 8 + ] + ] +); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, + 'qty' => 5, + 'value' => 5 + ] + ] +); +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => \Magento\Customer\Model\Group::NOT_LOGGED_IN_ID, + 'qty' => 3, + 'value' => 5 + ] + ] +); +/** @var $tpExtensionAttributes */ +$tpExtensionAttributesFactory = $objectManager->create(ProductTierPriceExtensionFactory::class); +$tpExtensionAttributes = $tpExtensionAttributesFactory->create()->setPercentageValue(50); + +$tierPrices[] = $tierPriceFactory->create( + [ + 'data' => [ + 'customer_group_id' => \Magento\Customer\Model\Group::NOT_LOGGED_IN_ID, + 'qty' => 10 + ] + ] +)->setExtensionAttributes($tpExtensionAttributes); + /** @var $product \Magento\Catalog\Model\Product */ $product = $objectManager->create(\Magento\Catalog\Model\Product::class); $product->isObjectNew(true); @@ -25,28 +113,7 @@ $product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) ->setWeight(1) ->setShortDescription("Short description") ->setTaxClassId(0) - ->setTierPrice( - [ - [ - 'website_id' => 0, - 'cust_group' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, - 'price_qty' => 2, - 'price' => 8, - ], - [ - 'website_id' => 0, - 'cust_group' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, - 'price_qty' => 5, - 'price' => 5, - ], - [ - 'website_id' => 0, - 'cust_group' => \Magento\Customer\Model\Group::NOT_LOGGED_IN_ID, - 'price_qty' => 3, - 'price' => 5, - ], - ] - ) + ->setTierPrices($tierPrices) ->setDescription('Description with <b>html tag</b>') ->setMetaTitle('meta title') ->setMetaKeyword('meta keyword') diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Config/Source/Group/MultiselectTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Config/Source/Group/MultiselectTest.php index d337d9a764265d8d98f7e2d20a503d98fd0342fe..899572a500e6ba281912575973757065c823738f 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/Config/Source/Group/MultiselectTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Config/Source/Group/MultiselectTest.php @@ -18,13 +18,24 @@ class MultiselectTest extends \PHPUnit_Framework_TestCase $multiselect = Bootstrap::getObjectManager()->get( \Magento\Customer\Model\Config\Source\Group\Multiselect::class ); + + $options = $multiselect->toOptionArray(); + $optionsToCompare = []; + foreach ($options as $option) { + if (is_array($option['value'])) { + $optionsToCompare = array_merge($optionsToCompare, $option['value']); + } else { + $optionsToCompare[] = $option; + } + } + sort($optionsToCompare); $this->assertEquals( [ ['value' => 1, 'label' => 'General'], ['value' => 2, 'label' => 'Wholesale'], ['value' => 3, 'label' => 'Retailer'], ], - $multiselect->toOptionArray() + $optionsToCompare ); } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Config/Source/GroupTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Config/Source/GroupTest.php index b4674c7b6aae33b309777b1db4c78662a098a7ce..3d0776d058f44f1f22a46bf224c4233e7c91f4d7 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/Config/Source/GroupTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Config/Source/GroupTest.php @@ -16,14 +16,33 @@ class GroupTest extends \PHPUnit_Framework_TestCase { /** @var Group $group */ $group = Bootstrap::getObjectManager()->get(\Magento\Customer\Model\Config\Source\Group::class); - $this->assertEquals( - [ - ['value' => '', 'label' => '-- Please Select --'], - ['value' => 1, 'label' => 'General'], - ['value' => 2, 'label' => 'Wholesale'], - ['value' => 3, 'label' => 'Retailer'], - ], - $group->toOptionArray() + $options = $group->toOptionArray(); + $this->assertContainsOptionRecursive('', '-- Please Select --', $options); + } + + private function assertContainsOptionRecursive($expectedValue, $expectedLabel, array $values) + { + $this->assertTrue( + $this->hasOptionLabelRecursive($expectedValue, $expectedLabel, $values), + 'Label ' . $expectedLabel . ' not found' ); } + + private function hasOptionLabelRecursive($value, $label, array $values) + { + $hasLabel = false; + foreach ($values as $option) { + $this->assertArrayHasKey('label', $option); + $this->assertArrayHasKey('value', $option); + if (strpos((string)$option['label'], (string)$label) !== false) { + $this->assertEquals($value, $option['value']); + $hasLabel = true; + break; + } elseif (is_array($option['value'])) { + $hasLabel |= $this->hasOptionLabelRecursive($value, $label, $option['value']); + } + } + + return (bool)$hasLabel; + } } diff --git a/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/EavAbstractTest.php b/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/EavAbstractTest.php index c1e9be87a9ab6a1f5e4bb6ce6896dc23594b601e..e3810fc0766bd7a1028cbc209a092a655b5f5ca0 100644 --- a/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/EavAbstractTest.php +++ b/dev/tests/integration/testsuite/Magento/ImportExport/Model/Import/Entity/EavAbstractTest.php @@ -52,11 +52,17 @@ class EavAbstractTest extends \PHPUnit_Framework_TestCase $index = $attribute->getAttributeCode() == $indexAttributeCode ? 'value' : 'label'; $expectedOptions = []; foreach ($attribute->getSource()->getAllOptions(false) as $option) { - $expectedOptions[strtolower($option[$index])] = $option['value']; + if (is_array($option['value'])) { + foreach ($option['value'] as $value) { + $expectedOptions[strtolower($value[$index])] = $value['value']; + } + } else { + $expectedOptions[strtolower($option[$index])] = $option['value']; + } } $actualOptions = $this->_model->getAttributeOptions($attribute, [$indexAttributeCode]); - sort($expectedOptions); - sort($actualOptions); + asort($expectedOptions); + asort($actualOptions); $this->assertEquals($expectedOptions, $actualOptions); } }