diff --git a/app/code/Magento/Catalog/Block/Product/View.php b/app/code/Magento/Catalog/Block/Product/View.php index 8157ab4de07f2a10c9bc5e34541944698f0ab71c..964a444a0aef8b924819598634a0eea36f7ba247 100644 --- a/app/code/Magento/Catalog/Block/Product/View.php +++ b/app/code/Magento/Catalog/Block/Product/View.php @@ -7,7 +7,6 @@ namespace Magento\Catalog\Block\Product; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Category; -use Magento\Catalog\Model\Product; /** * Product View block @@ -29,6 +28,7 @@ class View extends AbstractProduct implements \Magento\Framework\DataObject\Iden /** * @var \Magento\Framework\Pricing\PriceCurrencyInterface + * @deprecated */ protected $priceCurrency; @@ -225,40 +225,34 @@ class View extends AbstractProduct implements \Magento\Framework\DataObject\Iden $config = [ 'productId' => $product->getId(), 'priceFormat' => $this->_localeFormat->getPriceFormat() - ]; + ]; return $this->_jsonEncoder->encode($config); } $tierPrices = []; $tierPricesList = $product->getPriceInfo()->getPrice('tier_price')->getTierPriceList(); foreach ($tierPricesList as $tierPrice) { - $tierPrices[] = $this->priceCurrency->convert($tierPrice['price']->getValue()); + $tierPrices[] = $tierPrice['price']->getValue(); } $config = [ - 'productId' => $product->getId(), + 'productId' => $product->getId(), 'priceFormat' => $this->_localeFormat->getPriceFormat(), - 'prices' => [ - 'oldPrice' => [ - 'amount' => $this->priceCurrency->convert( - $product->getPriceInfo()->getPrice('regular_price')->getAmount()->getValue() - ), + 'prices' => [ + 'oldPrice' => [ + 'amount' => $product->getPriceInfo()->getPrice('regular_price')->getAmount()->getValue(), 'adjustments' => [] ], - 'basePrice' => [ - 'amount' => $this->priceCurrency->convert( - $product->getPriceInfo()->getPrice('final_price')->getAmount()->getBaseAmount() - ), + 'basePrice' => [ + 'amount' => $product->getPriceInfo()->getPrice('final_price')->getAmount()->getBaseAmount(), 'adjustments' => [] ], 'finalPrice' => [ - 'amount' => $this->priceCurrency->convert( - $product->getPriceInfo()->getPrice('final_price')->getAmount()->getValue() - ), + 'amount' => $product->getPriceInfo()->getPrice('final_price')->getAmount()->getValue(), 'adjustments' => [] ] ], - 'idSuffix' => '_clone', - 'tierPrices' => $tierPrices + 'idSuffix' => '_clone', + 'tierPrices' => $tierPrices ]; $responseObject = new \Magento\Framework\DataObject(); diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml index eacc039255caf98525625e3a18f6dd7e43376e70..ea3e8077d0dc195f42e4cddb78135b6a0d0f9470 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml @@ -20,9 +20,9 @@ <input type="number" name="qty" id="qty" - maxlength="12" value="<?php /* @escapeNotVerified */ echo $block->getProductDefaultQty() * 1 ?>" - title="<?php /* @escapeNotVerified */ echo __('Qty') ?>" class="input-text qty" + title="<?php /* @escapeNotVerified */ echo __('Qty') ?>" + class="input-text qty" data-validate="<?php echo $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>" /> </div> @@ -58,4 +58,4 @@ } } </script> -<?php endif; ?> \ No newline at end of file +<?php endif; ?> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml index 0f221a393f5eb181f31a778ce4d01da1b0cc109b..ebe8f4570b02f39ca9acb39986d72b5c402b496d 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml @@ -17,7 +17,13 @@ <div class="field qty"> <label class="label" for="qty"><span><?php /* @escapeNotVerified */ echo __('Qty') ?></span></label> <div class="control"> - <input type="number" name="qty" id="qty" maxlength="12" value="" title="<?php /* @escapeNotVerified */ echo __('Qty') ?>" class="input-text qty" data-validate="{'required-number':true,digits:true}"/> + <input type="number" + name="qty" + id="qty" + value="" + title="<?php /* @escapeNotVerified */ echo __('Qty') ?>" + class="input-text qty" + data-validate="{'required-number':true,digits:true}"/> </div> </div> <?php endif; ?> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml index d83b552b05cd4be4e55262bfb4c56a86039113c6..21fafe4d37f1b2496f421026c410e974972bc59d 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml @@ -96,7 +96,6 @@ $canApplyMsrp = $helper->isShowBeforeOrderConfirm($product) && $helper->isMinima size="4" title="<?php echo $block->escapeHtml(__('Qty')); ?>" class="input-text qty" - maxlength="12" data-validate="{required:true,'validate-greater-than-zero':true}" data-role="cart-item-qty"/> </div> diff --git a/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html b/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html index 89789996e5248bb8ffec2ac9db18b52eec315d56..8796152eb60315266d870682924f44789c2f5dc7 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/minicart/item/default.html @@ -79,8 +79,7 @@ }, value: qty" type="number" size="4" - class="item-qty cart-item-qty" - maxlength="12"/> + class="item-qty cart-item-qty"> <button data-bind="attr: { id: 'update-cart-item-'+item_id, 'data-cart-item': item_id, diff --git a/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php index 1cc1cceefb39972cfc09d2c7f70463d2f3f76da7..51c68145b24cf5e72061a51647d4b311f39e60bf 100644 --- a/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php @@ -92,6 +92,18 @@ class Configurable extends \Magento\Catalog\Block\Product\View\AbstractView ); } + /** + * Get cache key informative items. + * + * @return array + */ + public function getCacheKeyInfo() + { + $parentData = parent::getCacheKeyInfo(); + $parentData[] = $this->priceCurrency->getCurrencySymbol(); + return $parentData; + } + /** * Get allowed attributes * diff --git a/app/code/Magento/Customer/view/frontend/web/js/model/customer-addresses.js b/app/code/Magento/Customer/view/frontend/web/js/model/customer-addresses.js index f6740550efca6556490481a9c6c0516d21b9b4e2..a96dca0002d69d30fd0aab7372020e787383512e 100644 --- a/app/code/Magento/Customer/view/frontend/web/js/model/customer-addresses.js +++ b/app/code/Magento/Customer/view/frontend/web/js/model/customer-addresses.js @@ -11,11 +11,13 @@ define( ], function($, ko, address) { "use strict"; + var isLoggedIn = ko.observable(window.isCustomerLoggedIn); + return { getAddressItems: function() { var items = []; - if (isLoggedIn) { + if (isLoggedIn()) { var customerData = window.customerData; if (Object.keys(customerData).length) { $.each(customerData.addresses, function (key, item) { @@ -23,8 +25,9 @@ define( }); } } + return items; } } } -); \ No newline at end of file +); diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php index 0d8d18df223767b2812f58356de3b7904090d8f7..1841a9efb6cf8424ebc7ef0b289495388da31db2 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php @@ -8,6 +8,7 @@ namespace Magento\Eav\Model\Entity\Attribute; use Magento\Framework\Api\AttributeValueFactory; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Serialize\SerializerInterface; /** * Entity/Attribute/Model - attribute abstract @@ -22,6 +23,11 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens { const TYPE_STATIC = 'static'; + /** + * Const for empty string value. + */ + const EMPTY_STRING = ''; + /** * Attribute name * @@ -111,6 +117,27 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens */ protected $dataObjectHelper; + /** + * Serializer Instance. + * + * @var SerializerInterface + */ + private $serializer; + + /** + * Array of attribute types that have empty string as a possible value. + * + * @var array + */ + private $emptyStringTypes = [ + 'int', + 'decimal', + 'datetime', + 'varchar', + 'text', + 'static', + ]; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -166,6 +193,21 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens $this->dataObjectHelper = $dataObjectHelper; } + /** + * Get Serializer instance. + * @deprecated + * + * @return SerializerInterface + */ + private function getSerializer() + { + if ($this->serializer === null) { + $this->serializer = \Magento\Framework\App\ObjectManager::getInstance()->create(SerializerInterface::class); + } + + return $this->serializer; + } + /** * Initialize resource model * @@ -202,6 +244,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens $this->_getResource()->loadByCode($this, $entityTypeId, $code); $this->_afterLoad(); \Magento\Framework\Profiler::stop('load_by_code'); + return $this; } @@ -226,6 +269,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens public function setAttributeId($data) { $this->_data['attribute_id'] = $data; + return $this; } @@ -372,6 +416,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens public function setAttributeSetId($id) { $this->_data['attribute_set_id'] = $id; + return $this; } @@ -392,6 +437,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens public function setEntityTypeId($id) { $this->_data['entity_type_id'] = $id; + return $this; } @@ -403,6 +449,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens public function setEntityType($type) { $this->setData('entity_type', $type); + return $this; } @@ -456,6 +503,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens public function setEntity($entity) { $this->_entity = $entity; + return $this; } @@ -469,6 +517,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens if (!$this->_entity) { $this->_entity = $this->getEntityType(); } + return $this->_entity; } @@ -546,6 +595,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens } $this->_source = $source->setAttribute($this); } + return $this->_source; } @@ -557,6 +607,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens public function usesSource() { $input = $this->getFrontendInput(); + return $input === 'select' || $input === 'multiselect' || $this->_getData('source_model') != ''; } @@ -588,17 +639,38 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens } /** + * Check if Value is empty. + * * @param array|null|bool|int|float|string $value * @return bool */ public function isValueEmpty($value) { - /** @var array $emptyStringTypes list of attribute types that treat empty string as a possible value */ - $emptyStringTypes = ['int', 'decimal', 'datetime', 'varchar', 'text', 'static']; return (is_array($value) && count($value) == 0) || $value === null || ($value === false && $this->getBackend()->getType() != 'int') - || ($value === '' && in_array($this->getBackend()->getType(), $emptyStringTypes)); + || ($value === self::EMPTY_STRING && $this->isInEmptyStringTypes()); + } + + /** + * Check if attribute empty value is valid. + * + * @param array|null|bool|int|float|string $value + * @return bool + */ + public function isAllowedEmptyTextValue($value) + { + return $this->isInEmptyStringTypes() && $value === self::EMPTY_STRING; + } + + /** + * Check is attribute type in allowed empty string types. + * + * @return bool + */ + private function isInEmptyStringTypes() + { + return in_array($this->getBackend()->getType(), $this->emptyStringTypes); } /** @@ -654,6 +726,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens if (!isset($this->_attributeIdCache[$cacheKey])) { $this->_attributeIdCache[$cacheKey] = $this->getResource()->getIdByCode($entityType, $code); } + return $this->_attributeIdCache[$cacheKey]; } @@ -686,6 +759,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens $this->_dataTable = $backendTable; } } + return $this->_dataTable; } @@ -700,6 +774,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens if ($this->usesSource() && $this->getBackendType() != self::TYPE_STATIC) { return $this->getSource()->getFlatColumns(); } + return $this->_getFlatColumnsDdlDefinition(); } @@ -723,60 +798,60 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens $size = $prop['LENGTH'] ? $prop['LENGTH'] : null; $columns[$this->getAttributeCode()] = [ - 'type' => $this->_resourceHelper->getDdlTypeByColumnType($type), - 'length' => $size, + 'type' => $this->_resourceHelper->getDdlTypeByColumnType($type), + 'length' => $size, 'unsigned' => $prop['UNSIGNED'] ? true : false, 'nullable' => $prop['NULLABLE'], - 'default' => $prop['DEFAULT'], - 'extra' => null, + 'default' => $prop['DEFAULT'], + 'extra' => null, ]; break; case 'datetime': $columns[$this->getAttributeCode()] = [ - 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_DATETIME, + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_DATETIME, 'unsigned' => false, 'nullable' => true, - 'default' => null, - 'extra' => null, + 'default' => null, + 'extra' => null, ]; break; case 'decimal': $columns[$this->getAttributeCode()] = [ - 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_DECIMAL, - 'length' => '12,4', + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_DECIMAL, + 'length' => '12,4', 'unsigned' => false, 'nullable' => true, - 'default' => null, - 'extra' => null, + 'default' => null, + 'extra' => null, ]; break; case 'int': $columns[$this->getAttributeCode()] = [ - 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, 'unsigned' => false, 'nullable' => true, - 'default' => null, - 'extra' => null, + 'default' => null, + 'extra' => null, ]; break; case 'text': $columns[$this->getAttributeCode()] = [ - 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 'unsigned' => false, 'nullable' => true, - 'default' => null, - 'extra' => null, - 'length' => \Magento\Framework\DB\Ddl\Table::MAX_TEXT_SIZE, + 'default' => null, + 'extra' => null, + 'length' => \Magento\Framework\DB\Ddl\Table::MAX_TEXT_SIZE, ]; break; case 'varchar': $columns[$this->getAttributeCode()] = [ - 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, - 'length' => '255', + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + 'length' => '255', 'unsigned' => false, 'nullable' => true, - 'default' => null, - 'extra' => null, + 'default' => null, + 'extra' => null, ]; break; default: @@ -804,61 +879,62 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens } $prop = $describe[$this->getAttributeCode()]; $columns[$this->getAttributeCode()] = [ - 'type' => $prop['DATA_TYPE'] . ($prop['LENGTH'] ? "({$prop['LENGTH']})" : ""), + 'type' => $prop['DATA_TYPE'] . ($prop['LENGTH'] ? "({$prop['LENGTH']})" : ""), 'unsigned' => $prop['UNSIGNED'] ? true : false, - 'is_null' => $prop['NULLABLE'], - 'default' => $prop['DEFAULT'], - 'extra' => null, + 'is_null' => $prop['NULLABLE'], + 'default' => $prop['DEFAULT'], + 'extra' => null, ]; break; case 'datetime': $columns[$this->getAttributeCode()] = [ - 'type' => 'datetime', + 'type' => 'datetime', 'unsigned' => false, - 'is_null' => true, - 'default' => null, - 'extra' => null, + 'is_null' => true, + 'default' => null, + 'extra' => null, ]; break; case 'decimal': $columns[$this->getAttributeCode()] = [ - 'type' => 'decimal(12,4)', + 'type' => 'decimal(12,4)', 'unsigned' => false, - 'is_null' => true, - 'default' => null, - 'extra' => null, + 'is_null' => true, + 'default' => null, + 'extra' => null, ]; break; case 'int': $columns[$this->getAttributeCode()] = [ - 'type' => 'int', + 'type' => 'int', 'unsigned' => false, - 'is_null' => true, - 'default' => null, - 'extra' => null, + 'is_null' => true, + 'default' => null, + 'extra' => null, ]; break; case 'text': $columns[$this->getAttributeCode()] = [ - 'type' => 'text', + 'type' => 'text', 'unsigned' => false, - 'is_null' => true, - 'default' => null, - 'extra' => null, + 'is_null' => true, + 'default' => null, + 'extra' => null, ]; break; case 'varchar': $columns[$this->getAttributeCode()] = [ - 'type' => 'varchar(255)', + 'type' => 'varchar(255)', 'unsigned' => false, - 'is_null' => true, - 'default' => null, - 'extra' => null, + 'is_null' => true, + 'default' => null, + 'extra' => null, ]; break; default: break; } + return $columns; } @@ -945,6 +1021,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens foreach ($this->_storeManager->getStores() as $store) { $this->getFlatUpdateSelect($store->getId()); } + return $this; } @@ -955,6 +1032,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens if ($this->usesSource()) { return $this->getSource()->getFlatUpdateSelect($store); } + return $this->_getResource()->getFlatUpdateSelect($this, $store); } @@ -1064,6 +1142,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens } else { $this->setData(self::OPTIONS, $options); } + return $this; } @@ -1086,6 +1165,7 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens ); $dataObjects[] = $optionDataObject; } + return $dataObjects; } @@ -1195,8 +1275,9 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens if (is_array($rules)) { return $rules; } elseif (!empty($rules)) { - return unserialize($rules); + return $this->getSerializer()->unserialize($rules); } + return []; } diff --git a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php index c775a24a03c465dad45640a7f634823cb8b43ef8..bcaf11aaf5532be296fe19ac6d0bb451b6a20b13 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php @@ -125,55 +125,65 @@ class UpdateHandler implements AttributeInterface : null; // @todo verify is it normal to not have attributer_set_id $snapshot = $this->readSnapshot->execute($entityType, $entityDataForSnapshot); foreach ($this->getAttributes($entityType, $attributeSetId) as $attribute) { - if ($attribute->isStatic()) { - continue; - } - /** - * Only scalar values can be stored in generic tables - */ - if (isset($entityData[$attribute->getAttributeCode()]) - && !is_scalar($entityData[$attribute->getAttributeCode()])) { + $code = $attribute->getAttributeCode(); + $isAllowedValueType = array_key_exists($code, $entityData) + && (is_scalar($entityData[$code]) || $entityData[$code] === null); + + if ($attribute->isStatic() || !$isAllowedValueType) { continue; } - if (isset($snapshot[$attribute->getAttributeCode()]) - && $snapshot[$attribute->getAttributeCode()] !== false - && (array_key_exists($attribute->getAttributeCode(), $entityData) - && $attribute->isValueEmpty($entityData[$attribute->getAttributeCode()])) - ) { - $this->attributePersistor->registerDelete( - $entityType, - $entityData[$metadata->getLinkField()], - $attribute->getAttributeCode() - ); - } - if ((!array_key_exists($attribute->getAttributeCode(), $snapshot) - || $snapshot[$attribute->getAttributeCode()] === false) - && array_key_exists($attribute->getAttributeCode(), $entityData) - && !$attribute->isValueEmpty($entityData[$attribute->getAttributeCode()]) - ) { - $this->attributePersistor->registerInsert( - $entityType, - $entityData[$metadata->getLinkField()], - $attribute->getAttributeCode(), - $entityData[$attribute->getAttributeCode()] - ); - } - if (array_key_exists($attribute->getAttributeCode(), $snapshot) - && $snapshot[$attribute->getAttributeCode()] !== false - && array_key_exists($attribute->getAttributeCode(), $entityData) - && $snapshot[$attribute->getAttributeCode()] != $entityData[$attribute->getAttributeCode()] - && !$attribute->isValueEmpty($entityData[$attribute->getAttributeCode()]) - ) { - $this->attributePersistor->registerUpdate( - $entityType, - $entityData[$metadata->getLinkField()], - $attribute->getAttributeCode(), - $entityData[$attribute->getAttributeCode()] - ); + + $newValue = $entityData[$code]; + $isValueEmpty = $attribute->isValueEmpty($newValue); + $isAllowedEmptyStringValue = $attribute->isAllowedEmptyTextValue($newValue); + + if (array_key_exists($code, $snapshot)) { + $snapshotValue = $snapshot[$code]; + /** + * 'FALSE' value for attributes can't be update or delete + */ + if ($snapshotValue === false) { + continue; + } + + if (!$isValueEmpty || $isAllowedEmptyStringValue) { + /** + * NOT Updated value for attributes not need to update + */ + if ($snapshotValue === $newValue) { + continue; + } + + $this->attributePersistor->registerUpdate( + $entityType, + $entityData[$metadata->getLinkField()], + $code, + $newValue + ); + } else { + $this->attributePersistor->registerDelete( + $entityType, + $entityData[$metadata->getLinkField()], + $code + ); + } + } else { + /** + * Only not empty value of attribute is insertable + */ + if (!$isValueEmpty || $isAllowedEmptyStringValue) { + $this->attributePersistor->registerInsert( + $entityType, + $entityData[$metadata->getLinkField()], + $code, + $newValue + ); + } } } $this->attributePersistor->flush($entityType, $context); } + return $this->getReadHandler()->execute($entityType, $entityData, $arguments); } @@ -189,6 +199,7 @@ class UpdateHandler implements AttributeInterface if (!$this->readHandler) { $this->readHandler = ObjectManager::getInstance()->get(ReadHandler::class); } + return $this->readHandler; } } diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php index a10bafacda42dc2b72fcc285307a0250cae3074b..3c0e77408ba2dfbbe68651107cfa1a1d7a885588 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php @@ -144,19 +144,33 @@ class AbstractAttributeTest extends \PHPUnit_Framework_TestCase public function testGetValidationRulesWhenRuleIsSerialized() { - $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $rule = 'some value'; - $model = $objectManagerHelper->getObject( - \Magento\Catalog\Model\Entity\Attribute::class, - [ - 'data' => [ - \Magento\Eav\Api\Data\AttributeInterface::VALIDATE_RULES => serialize($rule) - ] + $rule = serialize('some value'); + $expected = 'some test result'; - ] - ); + $modelClassName = \Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class; + $model = $this->getMockForAbstractClass($modelClassName, [], '', false); - $this->assertEquals($rule, $model->getValidationRules()); + $serializerMock = $this->getMock(\Magento\Framework\Serialize\SerializerInterface::class); + + $reflection = new \ReflectionClass($modelClassName); + $reflectionProperty = $reflection->getProperty('serializer'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($model, $serializerMock); + + $model->setData(\Magento\Eav\Api\Data\AttributeInterface::VALIDATE_RULES, $rule); + + $serializerMock->method('unserialize') + ->with($rule) + ->willReturn($expected); + + $this->assertEquals($expected, $model->getValidationRules()); + + $data = ['test array']; + $model->setData(\Magento\Eav\Api\Data\AttributeInterface::VALIDATE_RULES, $data); + $this->assertEquals($data, $model->getValidationRules()); + + $model->setData(\Magento\Eav\Api\Data\AttributeInterface::VALIDATE_RULES, null); + $this->assertEquals([], $model->getValidationRules()); } public function testGetValidationRulesWhenRuleIsEmpty() diff --git a/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml b/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml index 58fbb4511e54ca06afdf0d3f8c864bb9e1a8bee4..05428e248b69c996af9c0863e9d48ca261777538 100644 --- a/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml +++ b/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml @@ -46,9 +46,9 @@ <td data-th="<?php echo $block->escapeHtml(__('Qty')); ?>" class="col qty"> <?php if ($_item->isSaleable()) : ?> <div class="control qty"> - <input type="number" name="super_group[<?php /* @escapeNotVerified */ echo $_item->getId() ?>]" + <input type="number" + name="super_group[<?php /* @escapeNotVerified */ echo $_item->getId() ?>]" data-selector="super_group[<?php /* @escapeNotVerified */ echo $_item->getId() ?>]" - maxlength="12" value="<?php /* @escapeNotVerified */ echo $_item->getQty() * 1 ?>" title="<?php /* @escapeNotVerified */ echo __('Qty') ?>" class="input-text qty" diff --git a/app/code/Magento/Sales/Block/Adminhtml/Transactions/Detail.php b/app/code/Magento/Sales/Block/Adminhtml/Transactions/Detail.php index 70eb41e8fcbdf6f8b55c00a8bd291a2ceef68df1..e97a1a2f9d64c9d60c82869ed2ff882dcecd5979 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Transactions/Detail.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Transactions/Detail.php @@ -131,7 +131,7 @@ class Detail extends \Magento\Backend\Block\Widget\Container $this->setOrderIncrementIdHtml($this->escapeHtml($this->_txn->getOrder()->getIncrementId())); - $this->setTxnTypeHtml($this->escapeHtml($this->_txn->getTxnType())); + $this->setTxnTypeHtml($this->escapeHtml(__($this->_txn->getTxnType()))); $this->setOrderIdUrlHtml( $this->escapeHtml($this->getUrl('sales/order/view', ['order_id' => $this->_txn->getOrderId()])) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js b/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js index 01c8ef29029a9ef34f82885e3efbada75774a88b..209b05773a497b8e02d36cdd4adba10eab107779 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/wysiwyg.js @@ -93,7 +93,7 @@ define([ /* eslint-disable no-undef */ if (tinyMCE) { _.each(tinyMCE.activeEditor.controlManager.controls, function (property, index, controls) { - controls[property].setDisabled(status); + controls[property.id].setDisabled(status); }); tinyMCE.activeEditor.getBody().setAttribute('contenteditable', !status); diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/CustomOptions.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/CustomOptions.xml index ae9c2238c9e008ac8086d365a1b1547d3309762b..b977e23166f45ec9c471ea08c5e7dc7516a728c0 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/CustomOptions.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/CustomOptions.xml @@ -382,5 +382,22 @@ </item> </field> </dataset> + + <dataset name="not_required_text_option"> + <field name="0" xsi:type="array"> + <item name="title" xsi:type="string">Test1 option %isolation%</item> + <item name="is_require" xsi:type="string">No</item> + <item name="type" xsi:type="string">Text/Field</item> + <item name="options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="price" xsi:type="string">10</item> + <item name="price_type" xsi:type="string">Fixed</item> + <item name="sku" xsi:type="string">sku1_%isolation%</item> + <item name="max_characters" xsi:type="string">45</item> + </item> + </item> + </field> + </dataset> + </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertCurrencyRateAppliedOnProductPage.php b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertCurrencyRateAppliedOnProductPage.php new file mode 100644 index 0000000000000000000000000000000000000000..6f3cfc48cdd553fea81ef9681f24e5799273af5e --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Constraint/AssertCurrencyRateAppliedOnProductPage.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\ConfigurableProduct\Test\Constraint; + +use Magento\Catalog\Test\Page\Product\CatalogProductView; +use Magento\Cms\Test\Page\CmsIndex; +use Magento\CurrencySymbol\Test\Fixture\CurrencySymbolEntity; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Fixture\InjectableFixture; + +/** + * Assert currency rate applied on configurable product page. + */ +class AssertCurrencyRateAppliedOnProductPage extends AbstractConstraint +{ + /** + * Assert currency rate applied on configurable product page. + * + * @param BrowserInterface $browser + * @param InjectableFixture $product + * @param CatalogProductView $view + * @param CmsIndex $cmsIndex + * @param CurrencySymbolEntity $baseCurrency + * @param array $configuredPrices + * @param string $basePrice + */ + public function processAssert( + BrowserInterface $browser, + InjectableFixture $product, + CatalogProductView $view, + CmsIndex $cmsIndex, + CurrencySymbolEntity $baseCurrency, + array $configuredPrices, + $basePrice + ) { + $browser->open($_ENV['app_frontend_url'] . $product->getUrlKey() . '.html'); + $this->assertPrice($view, $basePrice); + + $view->getViewBlock()->configure($product); + $this->assertPrice($view, $configuredPrices['custom_currency']); + + $cmsIndex->getCurrencyBlock()->switchCurrency($baseCurrency); + $view->getViewBlock()->configure($product); + $this->assertPrice($view, $configuredPrices['base_currency']); + } + + /** + * Assert price. + * + * @param CatalogProductView $view + * @param string $price + * @param string $currency [optional] + */ + public function assertPrice(CatalogProductView $view, $price, $currency = '') + { + \PHPUnit_Framework_Assert::assertEquals( + $price, + $view->getViewBlock()->getPriceBlock()->getPrice($currency), + 'Wrong price is displayed on Product page.' + ); + } + + /** + * Returns a string representation of successful assertion. + * + * @return string + */ + public function toString() + { + return "Currency rate has been applied correctly on Configurable Product page."; + } +} diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateCurrencyRateTest.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateCurrencyRateTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..6ba7729120ae909591f0703c36b83b51f2ea8aa0 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/CreateCurrencyRateTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Directory\Test\TestCase\CreateCurrencyRateTest" summary="Create Currency Rate" ticketId="MAGETWO-36824"> + <variation name="CreateCurrencyRateTestVariation3"> + <data name="currencyRate/data/currency_from" xsi:type="string">USD</data> + <data name="currencyRate/data/currency_to" xsi:type="string">UAH</data> + <data name="currencyRate/data/rate" xsi:type="number">2.000</data> + <data name="currencySymbol/dataSet" xsi:type="string">currency_symbols_uah</data> + <data name="product" xsi:type="string">configurableProduct::default</data> + <data name="config/dataset" xsi:type="string">config_base_currency_us_display_currency_uah</data> + <data name="baseCurrency/data/code" xsi:type="string">USD</data> + <data name="basePrice" xsi:type="string">₴80.00</data> + <data name="configuredPrices" xsi:type="array"> + <item name="custom_currency" xsi:type="string">₴80.00</item> + <item name="base_currency" xsi:type="string">$40.00</item> + </data> + <data name="tag" xsi:type="string">test_type:acceptance_test</data> + <constraint name="Magento\Directory\Test\Constraint\AssertCurrencyRateSuccessSaveMessage" /> + <constraint name="Magento\ConfigurableProduct\Test\Constraint\AssertCurrencyRateAppliedOnProductPage" /> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/Repository/ConfigData.xml index 9df42574840d692ced41a85979d63de6bf7c467f..3a099d79d370a97b9d75afb419bd36860e559a1a 100644 --- a/dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/Repository/ConfigData.xml +++ b/dev/tests/functional/tests/app/Magento/CurrencySymbol/Test/Repository/ConfigData.xml @@ -48,6 +48,18 @@ <item name="US Dollar" xsi:type="string">USD</item> </item> </field> + <field name="currency/options/base" xsi:type="array"> + <item name="scope" xsi:type="string">currency</item> + <item name="label" xsi:type="string">US Dollar</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="value" xsi:type="string">USD</item> + </field> + <field name="currency/options/default" xsi:type="array"> + <item name="scope" xsi:type="string">currency</item> + <item name="label" xsi:type="string">US Dollar</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="value" xsi:type="string">USD</item> + </field> </dataset> <dataset name="config_base_currency_ch"> diff --git a/dev/tests/functional/tests/app/Magento/Directory/Test/Constraint/AssertCurrencyRateAppliedOnProductPage.php b/dev/tests/functional/tests/app/Magento/Directory/Test/Constraint/AssertCurrencyRateAppliedOnProductPage.php new file mode 100644 index 0000000000000000000000000000000000000000..4b8f3825df8bab870a272e3db20864118db25079 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Directory/Test/Constraint/AssertCurrencyRateAppliedOnProductPage.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Directory\Test\Constraint; + +use Magento\Catalog\Test\Page\Product\CatalogProductView; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Fixture\InjectableFixture; + +/** + * Assert currency rate applied on product page. + */ +class AssertCurrencyRateAppliedOnProductPage extends AbstractConstraint +{ + /** + * Assert currency rate applied on product page. + * + * @param BrowserInterface $browser + * @param InjectableFixture $product + * @param CatalogProductView $view + * @param string $basePrice + */ + public function processAssert( + BrowserInterface $browser, + InjectableFixture $product, + CatalogProductView $view, + $basePrice + ) { + $browser->open($_ENV['app_frontend_url'] . $product->getUrlKey() . '.html'); + $this->assertPrice($view, $basePrice); + } + + /** + * Assert price. + * + * @param CatalogProductView $view + * @param string $price + * @param string $currency [optional] + */ + public function assertPrice(CatalogProductView $view, $price, $currency = '') + { + \PHPUnit_Framework_Assert::assertEquals( + $price, + $view->getViewBlock()->getPriceBlock()->getPrice($currency), + 'Wrong price is displayed on Product page.' + ); + } + + /** + * Returns a string representation of successful assertion. + * + * @return string + */ + public function toString() + { + return "Currency rate has been applied correctly on Product page."; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Directory/Test/Repository/ConfigData.xml b/dev/tests/functional/tests/app/Magento/Directory/Test/Repository/ConfigData.xml new file mode 100644 index 0000000000000000000000000000000000000000..7046437a0f4e2916aaa21e7eca1b902794aa8b9d --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Directory/Test/Repository/ConfigData.xml @@ -0,0 +1,33 @@ +<?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="../../../../../../vendor/magento/mtf/Magento/Mtf/Repository/etc/repository.xsd"> + <repository class="Magento\Config\Test\Repository\ConfigData"> + <dataset name="config_base_currency_us_display_currency_uah"> + <field name="currency/options/allow" xsi:type="array"> + <item name="scope" xsi:type="string">currency</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="value" xsi:type="array"> + <item name="Ukrainian Hryvnia" xsi:type="string">UAH</item> + <item name="US Dollar" xsi:type="string">USD</item> + </item> + </field> + <field name="currency/options/base" xsi:type="array"> + <item name="scope" xsi:type="string">currency</item> + <item name="label" xsi:type="string">US Dollar</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="value" xsi:type="string">USD</item> + </field> + <field name="currency/options/default" xsi:type="array"> + <item name="scope" xsi:type="string">currency</item> + <item name="label" xsi:type="string">Ukrainian Hryvnia</item> + <item name="scope_id" xsi:type="number">1</item> + <item name="value" xsi:type="string">UAH</item> + </field> + </dataset> + </repository> +</config> diff --git a/dev/tests/functional/tests/app/Magento/Directory/Test/TestCase/CreateCurrencyRateTest.php b/dev/tests/functional/tests/app/Magento/Directory/Test/TestCase/CreateCurrencyRateTest.php index bc40a2ab98f6d913b80736346c7c06ef6f1c7f9e..c7841934035cadcf40b55931f748948427504652 100644 --- a/dev/tests/functional/tests/app/Magento/Directory/Test/TestCase/CreateCurrencyRateTest.php +++ b/dev/tests/functional/tests/app/Magento/Directory/Test/TestCase/CreateCurrencyRateTest.php @@ -6,11 +6,12 @@ namespace Magento\Directory\Test\TestCase; +use Magento\Catalog\Test\TestStep\CreateProductsStep; use Magento\Config\Test\Fixture\ConfigData; use Magento\Mtf\TestCase\Injectable; use Magento\Directory\Test\Fixture\CurrencyRate; -use Magento\Catalog\Test\Fixture\CatalogProductSimple; use Magento\CurrencySymbol\Test\Page\Adminhtml\SystemCurrencyIndex; +use Magento\Mtf\TestStep\TestStepFactory; /** * Preconditions: @@ -41,35 +42,48 @@ class CreateCurrencyRateTest extends Injectable */ protected $currencyIndexPage; + /** + * Test step factory. + * + * @var TestStepFactory + */ + private $stepFactory; + /** * Inject data. * * @param SystemCurrencyIndex $currencyIndexPage - * @return void + * @param TestStepFactory $stepFactory */ - public function __inject(SystemCurrencyIndex $currencyIndexPage) + public function __inject(SystemCurrencyIndex $currencyIndexPage, TestStepFactory $stepFactory) { $this->currencyIndexPage = $currencyIndexPage; + $this->stepFactory = $stepFactory; } /** * Create currency rate test. * * @param CurrencyRate $currencyRate - * @param CatalogProductSimple $product - * @param $config - * @return void + * @param ConfigData $config + * @param string $product + * @param array $productData [optional] + * @return array */ - public function test(CurrencyRate $currencyRate, CatalogProductSimple $product, ConfigData $config) + public function test(CurrencyRate $currencyRate, ConfigData $config, $product, array $productData = []) { // Preconditions: - $product->persist(); + $product = $this->stepFactory + ->create(CreateProductsStep::class, ['products' => [$product], 'data' => $productData]) + ->run()['products'][0]; $config->persist(); // Steps: $this->currencyIndexPage->open(); $this->currencyIndexPage->getCurrencyRateForm()->fill($currencyRate); $this->currencyIndexPage->getFormPageActions()->save(); + + return ['product' => $product]; } /** diff --git a/dev/tests/functional/tests/app/Magento/Directory/Test/TestCase/CreateCurrencyRateTest.xml b/dev/tests/functional/tests/app/Magento/Directory/Test/TestCase/CreateCurrencyRateTest.xml index dd42950c18a78175ce51fb7cfce85fe7fbc85a0c..e984cad9ca327aa2a0896e44fc85e365e9f24503 100644 --- a/dev/tests/functional/tests/app/Magento/Directory/Test/TestCase/CreateCurrencyRateTest.xml +++ b/dev/tests/functional/tests/app/Magento/Directory/Test/TestCase/CreateCurrencyRateTest.xml @@ -13,12 +13,25 @@ <data name="currencyRate/data/currency_to" xsi:type="string">EUR</data> <data name="currencyRate/data/rate" xsi:type="number">0.8</data> <data name="currencySymbol/dataset" xsi:type="string">currency_symbols_eur</data> - <data name="product/dataset" xsi:type="string">simple_10_dollar</data> + <data name="product" xsi:type="string">catalogProductSimple::simple_10_dollar</data> <data name="config/dataset" xsi:type="string">config_currency_symbols_usd_and_eur</data> <data name="basePrice" xsi:type="string">$10.00</data> <data name="convertedPrice" xsi:type="string">€8.00</data> <constraint name="Magento\Directory\Test\Constraint\AssertCurrencyRateSuccessSaveMessage" /> <constraint name="Magento\Directory\Test\Constraint\AssertCurrencyRateAppliedOnCatalogPage" /> </variation> + <variation name="CreateCurrencyRateTestVariation2"> + <data name="currencyRate/data/currency_from" xsi:type="string">USD</data> + <data name="currencyRate/data/currency_to" xsi:type="string">UAH</data> + <data name="currencyRate/data/rate" xsi:type="number">2.000</data> + <data name="currencySymbol/dataSet" xsi:type="string">currency_symbols_uah</data> + <data name="product" xsi:type="string">catalogProductSimple::simple_10_dollar</data> + <data name="productData/0/custom_options/dataset" xsi:type="string">not_required_text_option</data> + <data name="config/dataset" xsi:type="string">config_base_currency_us_display_currency_uah</data> + <data name="basePrice" xsi:type="string">₴20.00</data> + <data name="tag" xsi:type="string">test_type:acceptance_test</data> + <constraint name="Magento\Directory\Test\Constraint\AssertCurrencyRateSuccessSaveMessage" /> + <constraint name="Magento\Directory\Test\Constraint\AssertCurrencyRateAppliedOnProductPage" /> + </variation> </testCase> </config> diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php index f84c661126b04b9a30ec2ca84ef7d3f570d2a1bb..5db10afdee00284a4faa7249e273bb3d47ab5daf 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/CategoryTest.php @@ -233,7 +233,7 @@ class CategoryTest extends \Magento\TestFramework\TestCase\AbstractBackendContro 'display_mode' => true, 'meta_title' => true, 'custom_design' => true, - 'page_layout' => false, + 'page_layout' => true, 'is_active' => true, 'include_in_menu' => true, 'landing_page' => true, @@ -242,7 +242,7 @@ class CategoryTest extends \Magento\TestFramework\TestCase\AbstractBackendContro 'description' => true, 'meta_keywords' => true, 'meta_description' => true, - 'custom_layout_update' => false, + 'custom_layout_update' => true, 'custom_design_from' => true, 'custom_design_to' => true, 'filter_price_range' => false diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/dropdown_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/dropdown_attribute.php new file mode 100644 index 0000000000000000000000000000000000000000..56e84046b255bf89c5f6aa6c1cb75cb7c7d584ba --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/dropdown_attribute.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +/* Create attribute */ +/** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */ +$attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class +); + +if (!$attribute->loadByCode(4, 'dropdown_attribute')->getId()) { + /** @var $installer \Magento\Catalog\Setup\CategorySetup */ + $installer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Setup\CategorySetup::class + ); + + $attribute->setData( + [ + 'attribute_code' => 'dropdown_attribute', + 'entity_type_id' => $installer->getEntityTypeId('catalog_product'), + 'is_global' => 0, + 'is_user_defined' => 1, + 'frontend_input' => 'select', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 0, + 'is_visible_in_advanced_search' => 0, + 'is_comparable' => 0, + 'is_filterable' => 0, + 'is_filterable_in_search' => 0, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 0, + 'used_in_product_listing' => 0, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Drop-Down Attribute'], + 'backend_type' => 'varchar', + 'backend_model' => \Magento\Eav\Model\Entity\Attribute\Backend\ArrayBackend::class, + 'option' => [ + 'value' => [ + 'option_1' => ['Option 1'], + 'option_2' => ['Option 2'], + 'option_3' => ['Option 3'], + ], + 'order' => [ + 'option_1' => 1, + 'option_2' => 2, + 'option_3' => 3, + ], + ], + ] + ); + $attribute->save(); + + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup('catalog_product', 'Default', 'Attributes', $attribute->getId()); +} diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/ResourceModel/UpdateHandlerTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/ResourceModel/UpdateHandlerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..1f8b20b7b041119a0394e0d3d8b83de3aeb727c4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Eav/Model/ResourceModel/UpdateHandlerTest.php @@ -0,0 +1,256 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Eav\Model\ResourceModel; + +use Magento\TestFramework\Helper\Bootstrap; + +/** + * @magentoAppArea adminhtml + * @magentoAppIsolation enabled + */ +class UpdateHandlerTest extends \Magento\TestFramework\Indexer\TestCase +{ + /** + * @covers \Magento\Eav\Model\ResourceModel\UpdateHandler::execute + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @dataProvider getAllStoresDataProvider + * @param $code + * @param $snapshotValue + * @param $newValue + * @param $expected + */ + public function testExecuteProcessForAllStores($code, $snapshotValue, $newValue, $expected) + { + if ($snapshotValue !== '-') { + $entity = Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); + $entity->setStoreId(0); + $entity->load(1); + $entity->setData($code, $snapshotValue); + $entity->save(); + } + + $entity = Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); + $entity->setStoreId(0); + $entity->load(1); + + $updateHandler = Bootstrap::getObjectManager()->create(UpdateHandler::class); + $entityData = array_merge($entity->getData(), [$code => $newValue]); + $updateHandler->execute(\Magento\Catalog\Api\Data\ProductInterface::class, $entityData); + + $resultEntity = Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); + $resultEntity->setStoreId(0); + $resultEntity->load(1); + + $this->assertSame($expected, $resultEntity->getData($code)); + } + + /** + * @covers \Magento\Eav\Model\ResourceModel\UpdateHandlerTest::execute + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Store/_files/second_store.php + * @dataProvider getCustomStoreDataProvider + * @param $code + * @param $snapshotValue + * @param $newValue + * @param $expected + */ + public function testExecuteProcessForCustomStore($code, $snapshotValue, $newValue, $expected) + { + $store = Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class); + $store->load('fixture_second_store', 'code'); + + Bootstrap::getObjectManager() + ->create(\Magento\CatalogSearch\Model\Indexer\Fulltext\Processor::class) + ->reindexAll(); + + if ($snapshotValue !== '-') { + $entity = Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); + $entity->setStoreId($store->getId()); + $entity->load(1); + $entity->setData($code, $snapshotValue); + $entity->save(); + } + + $entity = Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); + $entity->setStoreId($store->getId()); + $entity->load(1); + + $updateHandler = Bootstrap::getObjectManager()->create(UpdateHandler::class); + $entityData = array_merge($entity->getData(), [$code => $newValue]); + $updateHandler->execute(\Magento\Catalog\Api\Data\ProductInterface::class, $entityData); + + $resultEntity = Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); + $resultEntity->setStoreId($store->getId()); + $resultEntity->load(1); + + $this->assertSame($expected, $resultEntity->getData($code)); + } + + /** + * @covers \Magento\Eav\Model\ResourceModel\UpdateHandlerTest::execute + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php + * @magentoDataFixture Magento/Store/_files/second_store.php + * @dataProvider getCustomAttributeDataProvider + * @param $code + * @param $defaultStoreValue + * @param $snapshotValue + * @param $newValue + * @param $expected + */ + public function testExecuteProcessForCustomAttributeInCustomStore( + $code, + $defaultStoreValue, + $snapshotValue, + $newValue, + $expected + ) { + $store = Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class); + $store->load('fixture_second_store', 'code'); + + Bootstrap::getObjectManager() + ->create(\Magento\CatalogSearch\Model\Indexer\Fulltext\Processor::class) + ->reindexAll(); + + $attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Model\ResourceModel\Eav\Attribute::class + ); + $attribute->loadByCode(4, $code); + + $options = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection::class + ); + $options->setAttributeFilter($attribute->getId()); + $optionIds = $options->getAllIds(); + + $entity = Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); + $entity->setStoreId(0); + $entity->load(1); + $entity->setData($code, $optionIds[$defaultStoreValue]); + $entity->save(); + + if ($snapshotValue !== '-') { + /** @var $options \Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection */ + $entity = Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); + $entity->setStoreId($store->getId()); + $entity->load(1); + + if ($snapshotValue) { + $snapshotValue = $optionIds[$snapshotValue]; + } + + $entity->setData($code, $snapshotValue); + $entity->save(); + } + + $entity = Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); + $entity->setStoreId($store->getId()); + $entity->load(1); + + $updateHandler = Bootstrap::getObjectManager()->create(UpdateHandler::class); + + if ($newValue) { + $newValue = $optionIds[$newValue]; + } + + $entityData = array_merge($entity->getData(), [$code => $newValue]); + $updateHandler->execute(\Magento\Catalog\Api\Data\ProductInterface::class, $entityData); + + $resultEntity = Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); + $resultEntity->setStoreId($store->getId()); + $resultEntity->load(1); + + if ($expected !== null) { + $expected = $optionIds[$expected]; + } + + $this->assertSame($expected, $resultEntity->getData($code)); + } + + /** + * @return array + */ + public function getAllStoresDataProvider() + { + return [ + ['description', '', 'not_empty_value', 'not_empty_value'], //0 + ['description', '', '', null], //1 + ['description', '', null, null], //2 + ['description', '', false, null], //3 + + ['description', 'not_empty_value', 'not_empty_value2', 'not_empty_value2'], //4 + ['description', 'not_empty_value', '', null], //5 + ['description', 'not_empty_value', null, null], //6 + ['description', 'not_empty_value', false, null], //7 + + ['description', null, 'not_empty_value', 'not_empty_value'], //8 + ['description', null, '', null], //9 + ['description', null, false, null], //10 + + ['description', false, 'not_empty_value', 'not_empty_value'], //11 + ['description', false, '', null], //12 + ['description', false, null, null], //13 + ]; + } + + /** + * @return array + */ + public function getCustomStoreDataProvider() + { + return [ + ['description', '', 'not_empty_value', 'not_empty_value'], //0 + ['description', '', '', null], //1 + ['description', '', null, 'Description with <b>html tag</b>'], //2 + ['description', '', false, 'Description with <b>html tag</b>'], //3 + + ['description', 'not_empty_value', 'not_empty_value2', 'not_empty_value2'], //4 + ['description', 'not_empty_value', '', null], //5 + ['description', 'not_empty_value', null, 'Description with <b>html tag</b>'], //6 + ['description', 'not_empty_value', false, 'Description with <b>html tag</b>'], //7 + + ['description', null, 'not_empty_value', 'not_empty_value'], //8 + ['description', null, '', null], //9 + ['description', null, false, 'Description with <b>html tag</b>'], //10 + + ['description', false, 'not_empty_value', 'not_empty_value'], //11 + ['description', false, '', null], //12 + ['description', false, null, 'Description with <b>html tag</b>'], //13 + ]; + } + + /** + * @return array + */ + public function getCustomAttributeDataProvider() + { + return [ + ['dropdown_attribute', 0, '', 1, 1], //0 + ['dropdown_attribute', 0, '', '', null], //1 + ['dropdown_attribute', 0, '', null, 0], //2 + ['dropdown_attribute', 0, '', false, 0], //3 + + ['dropdown_attribute', 0, 1, 2, 2], //4 + ['dropdown_attribute', 0, 1, '', null], //5 + ['dropdown_attribute', 0, 1, null, 0], //6 + ['dropdown_attribute', 0, 1, false, 0], //7 + + ['dropdown_attribute', 0, null, 1, 1], //8 + ['dropdown_attribute', 0, null, '', null], //9 + ['dropdown_attribute', 0, null, false, 0], //10 + + ['dropdown_attribute', 0, false, 1, 1], //11 + ['dropdown_attribute', 0, false, '', null], //12 + ['dropdown_attribute', 0, false, null, 0], //13 + + ['dropdown_attribute', 0, '-', 1, 1], //14 + ['dropdown_attribute', 0, '-', '', null], //15 + ['dropdown_attribute', 0, '-', null, 0], //16 + ['dropdown_attribute', 0, '-', false, 0], //17 + ]; + } +}