diff --git a/app/code/Magento/Catalog/Cron/DeleteOutdatedPriceValues.php b/app/code/Magento/Catalog/Cron/DeleteOutdatedPriceValues.php
new file mode 100644
index 0000000000000..f9f25c42c6eea
--- /dev/null
+++ b/app/code/Magento/Catalog/Cron/DeleteOutdatedPriceValues.php
@@ -0,0 +1,74 @@
+resource = $resource;
+ $this->attributeRepository = $attributeRepository;
+ $this->scopeConfig = $scopeConfig;
+ }
+
+ /**
+ * Delete all price values for non-admin stores if PRICE_SCOPE is global
+ *
+ * @return void
+ */
+ public function execute()
+ {
+ $priceScope = $this->scopeConfig->getValue(Store::XML_PATH_PRICE_SCOPE);
+ if ($priceScope == Store::PRICE_SCOPE_GLOBAL) {
+ /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $priceAttribute */
+ $priceAttribute = $this->attributeRepository
+ ->get(ProductAttributeInterface::ENTITY_TYPE_CODE, ProductAttributeInterface::CODE_PRICE);
+ $connection = $this->resource->getConnection();
+ $conditions = [
+ $connection->quoteInto('attribute_id = ?', $priceAttribute->getId()),
+ $connection->quoteInto('store_id != ?', Store::DEFAULT_STORE_ID),
+ ];
+
+ $connection->delete(
+ $priceAttribute->getBackend()->getTable(),
+ $conditions
+ );
+ }
+ }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Price.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Price.php
index 1cc512aae6031..058b8def8bbbc 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Price.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Price.php
@@ -5,7 +5,7 @@
*/
namespace Magento\Catalog\Model\Product\Attribute\Backend;
-use \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
+use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
/**
* Catalog product price attribute backend model
@@ -102,43 +102,27 @@ public function setScope($attribute)
}
/**
- * After Save Attribute manipulation
+ * After Save Price Attribute manipulation
+ * Processes product price attributes if price scoped to website and updates data when:
+ * * Price changed for non-default store view - will update price for all stores assigned to current website.
+ * * Price will be changed according to store currency even if price changed in product with default store id.
+ * * In a case when price was removed for non-default store (use default option checked) the default store price
+ * * will be used instead
*
* @param \Magento\Catalog\Model\Product $object
* @return $this
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function afterSave($object)
{
- $value = $object->getData($this->getAttribute()->getAttributeCode());
- /**
- * Orig value is only for existing objects
- */
- $oridData = $object->getOrigData();
- $origValueExist = $oridData && array_key_exists($this->getAttribute()->getAttributeCode(), $oridData);
- if ($object->getStoreId() != 0 || !$value || $origValueExist) {
- return $this;
- }
-
- if ($this->getAttribute()->getIsGlobal() == ScopedAttributeInterface::SCOPE_WEBSITE) {
- $baseCurrency = $this->_config->getValue(
- \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE,
- 'default'
- );
-
- $storeIds = $object->getStoreIds();
- if (is_array($storeIds)) {
- foreach ($storeIds as $storeId) {
- $storeCurrency = $this->_storeManager->getStore($storeId)->getBaseCurrencyCode();
- if ($storeCurrency == $baseCurrency) {
- continue;
- }
- $rate = $this->_currencyFactory->create()->load($baseCurrency)->getRate($storeCurrency);
- if (!$rate) {
- $rate = 1;
- }
- $newValue = $value * $rate;
- $object->addAttributeUpdate($this->getAttribute()->getAttributeCode(), $newValue, $storeId);
+ /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
+ $attribute = $this->getAttribute();
+ $attributeCode = $attribute->getAttributeCode();
+ $value = $object->getData($attributeCode);
+ if ($value && $value != $object->getOrigData($attributeCode)) {
+ if ($attribute->isScopeWebsite()) {
+ foreach ((array)$object->getWebsiteStoreIds() as $storeId) {
+ /** @var $object \Magento\Catalog\Model\Product */
+ $object->addAttributeUpdate($attributeCode, $value, $storeId);
}
}
}
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php b/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php
index 3d5e49985d27b..669e14be81317 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Repository.php
@@ -128,10 +128,6 @@ public function save(\Magento\Catalog\Api\Data\ProductAttributeInterface $attrib
}
$attribute->setDefaultFrontendLabel($frontendLabel);
}
- if (!$attribute->getIsUserDefined()) {
- // Unset attribute field for system attributes
- $attribute->setApplyTo(null);
- }
} else {
$attribute->setAttributeId(null);
diff --git a/app/code/Magento/Catalog/Observer/SwitchPriceAttributeScopeOnConfigChange.php b/app/code/Magento/Catalog/Observer/SwitchPriceAttributeScopeOnConfigChange.php
new file mode 100644
index 0000000000000..31e649bb73e12
--- /dev/null
+++ b/app/code/Magento/Catalog/Observer/SwitchPriceAttributeScopeOnConfigChange.php
@@ -0,0 +1,77 @@
+config = $config;
+ $this->productAttributeRepository = $productAttributeRepository;
+ $this->searchCriteriaBuilder = $searchCriteriaBuilder;
+ }
+
+ /**
+ * Change scope for all price attributes according to
+ * 'Catalog Price Scope' configuration parameter value
+ *
+ * @param EventObserver $observer
+ * @return void
+ */
+ public function execute(EventObserver $observer)
+ {
+ $this->searchCriteriaBuilder->addFilter('frontend_input', 'price');
+ $criteria = $this->searchCriteriaBuilder->create();
+
+ $scope = $this->config->getValue(Store::XML_PATH_PRICE_SCOPE);
+ $scope = ($scope == Store::PRICE_SCOPE_WEBSITE)
+ ? ProductAttributeInterface::SCOPE_WEBSITE_TEXT
+ : ProductAttributeInterface::SCOPE_GLOBAL_TEXT;
+
+ $priceAttributes = $this->productAttributeRepository->getList($criteria)->getItems();
+
+ /** @var ProductAttributeInterface $priceAttribute */
+ foreach ($priceAttributes as $priceAttribute) {
+ $priceAttribute->setScope($scope);
+ $this->productAttributeRepository->save($priceAttribute);
+ }
+ }
+}
diff --git a/app/code/Magento/Catalog/Setup/UpgradeData.php b/app/code/Magento/Catalog/Setup/UpgradeData.php
index ab32e8c5a6c7d..f61e28a10bce3 100644
--- a/app/code/Magento/Catalog/Setup/UpgradeData.php
+++ b/app/code/Magento/Catalog/Setup/UpgradeData.php
@@ -340,7 +340,7 @@ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface
}
if (version_compare($context->getVersion(), '2.0.7') < 0) {
- /** @var EavSetup $eavSetupF */
+ /** @var EavSetup $eavSetup */
$eavSetup= $this->eavSetupFactory->create(['setup' => $setup]);
$eavSetup->updateAttribute(
@@ -351,7 +351,32 @@ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface
]
);
}
-
+
+ if (version_compare($context->getVersion(), '2.1.3') < 0) {
+ /** @var \Magento\Catalog\Setup\CategorySetup $categorySetup */
+ $categorySetup = $this->categorySetupFactory->create(['setup' => $setup]);
+ $this->changePriceAttributeDefaultScope($categorySetup);
+ }
+
$setup->endSetup();
}
+
+ /**
+ * @param \Magento\Catalog\Setup\CategorySetup $categorySetup
+ * @return void
+ */
+ private function changePriceAttributeDefaultScope($categorySetup)
+ {
+ $entityTypeId = $categorySetup->getEntityTypeId(\Magento\Catalog\Model\Product::ENTITY);
+ foreach (['price', 'cost', 'special_price'] as $attributeCode) {
+ $attribute = $categorySetup->getAttribute($entityTypeId, $attributeCode);
+ $categorySetup->updateAttribute(
+ $entityTypeId,
+ $attribute['attribute_id'],
+ 'is_global',
+ \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL
+ );
+
+ }
+ }
}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/PriceTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/PriceTest.php
index 8dc8817786545..722cc1819029b 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/PriceTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Attribute/Backend/PriceTest.php
@@ -5,43 +5,57 @@
*/
namespace Magento\Catalog\Test\Unit\Model\Product\Attribute\Backend;
+/**
+ * Class PriceTest
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
class PriceTest extends \PHPUnit_Framework_TestCase
{
/**
* @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Price
*/
- protected $model;
+ private $model;
+
+ /**
+ * @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $attribute;
+
+ /**
+ * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $storeManager;
+
+ /**
+ * @var \Magento\Catalog\Model\Attribute\ScopeOverriddenValue|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $scopeOverriddenValue;
protected function setUp()
{
$objectHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
-
- // we want to use an actual implementation of \Magento\Framework\Locale\FormatInterface
- $scopeResolver = $this->getMockForAbstractClass('\Magento\Framework\App\ScopeResolverInterface', [], '', false);
- $localeResolver = $this->getMockForAbstractClass('\Magento\Framework\Locale\ResolverInterface', [], '', false);
- $currencyFactory = $this->getMock('\Magento\Directory\Model\CurrencyFactory', [], [], '', false);
- $localeFormat = $objectHelper->getObject(
- 'Magento\Framework\Locale\Format',
+ $localeFormat = $objectHelper->getObject(\Magento\Framework\Locale\Format::class);
+ $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class)
+ ->getMockForAbstractClass();
+ $this->scopeOverriddenValue = $this->getMockBuilder(
+ \Magento\Catalog\Model\Attribute\ScopeOverriddenValue::class
+ )
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->model = $objectHelper->getObject(
+ \Magento\Catalog\Model\Product\Attribute\Backend\Price::class,
[
- 'scopeResolver' => $scopeResolver,
- 'localeResolver' => $localeResolver,
- 'currencyFactory' => $currencyFactory,
+ 'localeFormat' => $localeFormat,
+ 'storeManager' => $this->storeManager,
+ 'scopeOverriddenValue' => $this->scopeOverriddenValue
]
);
- // the model we are testing
- $this->model = $objectHelper->getObject(
- 'Magento\Catalog\Model\Product\Attribute\Backend\Price',
- ['localeFormat' => $localeFormat]
- );
-
- $attribute = $this->getMockForAbstractClass(
- '\Magento\Eav\Model\Entity\Attribute\AbstractAttribute',
- [],
- '',
- false
- );
- $this->model->setAttribute($attribute);
+ $this->attribute = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class)
+ ->setMethods(['getAttributeCode', 'isScopeWebsite'])
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+ $this->model->setAttribute($this->attribute);
}
/**
@@ -81,7 +95,7 @@ public function dataProviderValidate()
*/
public function testValidateForFailure($value)
{
- $object = $this->getMock('Magento\Catalog\Model\Product', [], [], '', false);
+ $object = $this->getMock(\Magento\Catalog\Model\Product::class, [], [], '', false);
$object->expects($this->once())->method('getData')->willReturn($value);
$this->model->validate($object);
@@ -101,4 +115,69 @@ public function dataProviderValidateForFailure()
'negative Lebanon' => ['-1 234'],
];
}
+
+ public function testAfterSaveWithDifferentStores()
+ {
+ $newPrice = '9.99';
+ $attributeCode = 'price';
+ $defaultStoreId = 0;
+ $websiteStoreIds = [1, 2, 3];
+ $object = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)->disableOriginalConstructor()->getMock();
+ $object->expects($this->any())->method('getData')->with($attributeCode)->willReturn($newPrice);
+ $object->expects($this->any())->method('getOrigData')->with($attributeCode)->willReturn('7.77');
+ $object->expects($this->any())->method('getStoreId')->willReturn($defaultStoreId);
+ $object->expects($this->any())->method('getWebsiteStoreIds')->willReturn($websiteStoreIds);
+ $this->attribute->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode);
+ $this->attribute->expects($this->any())->method('isScopeWebsite')
+ ->willReturn(\Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE);
+
+ $object->expects($this->any())->method('addAttributeUpdate')->withConsecutive(
+ [
+ $this->equalTo($attributeCode),
+ $this->equalTo($newPrice),
+ $this->equalTo($websiteStoreIds[0])
+ ],
+ [
+ $this->equalTo($attributeCode),
+ $this->equalTo($newPrice),
+ $this->equalTo($websiteStoreIds[1])
+ ],
+ [
+ $this->equalTo($attributeCode),
+ $this->equalTo($newPrice),
+ $this->equalTo($websiteStoreIds[2])
+ ]
+ );
+ $this->assertEquals($this->model, $this->model->afterSave($object));
+ }
+
+ public function testAfterSaveWithOldPrice()
+ {
+ $attributeCode = 'price';
+
+ $object = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)->disableOriginalConstructor()->getMock();
+ $object->expects($this->any())->method('getData')->with($attributeCode)->willReturn('7.77');
+ $object->expects($this->any())->method('getOrigData')->with($attributeCode)->willReturn('7.77');
+ $this->attribute->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode);
+ $this->attribute->expects($this->any())->method('getIsGlobal')
+ ->willReturn(\Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE);
+
+ $object->expects($this->never())->method('addAttributeUpdate');
+ $this->assertEquals($this->model, $this->model->afterSave($object));
+ }
+
+ public function testAfterSaveWithGlobalPrice()
+ {
+ $attributeCode = 'price';
+
+ $object = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)->disableOriginalConstructor()->getMock();
+ $object->expects($this->any())->method('getData')->with($attributeCode)->willReturn('9.99');
+ $object->expects($this->any())->method('getOrigData')->with($attributeCode)->willReturn('7.77');
+ $this->attribute->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode);
+ $this->attribute->expects($this->any())->method('getIsGlobal')
+ ->willReturn(\Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL);
+
+ $object->expects($this->never())->method('addAttributeUpdate');
+ $this->assertEquals($this->model, $this->model->afterSave($object));
+ }
}
diff --git a/app/code/Magento/Catalog/etc/crontab.xml b/app/code/Magento/Catalog/etc/crontab.xml
index 2288ed4ebd8a8..b7d3b40de7cfe 100644
--- a/app/code/Magento/Catalog/etc/crontab.xml
+++ b/app/code/Magento/Catalog/etc/crontab.xml
@@ -13,5 +13,8 @@
0 0 * * *
+
+ * * * * *
+
diff --git a/app/code/Magento/Catalog/etc/events.xml b/app/code/Magento/Catalog/etc/events.xml
index 544abf6b9e069..9d618b9a4653c 100644
--- a/app/code/Magento/Catalog/etc/events.xml
+++ b/app/code/Magento/Catalog/etc/events.xml
@@ -51,4 +51,7 @@
+
+
+
diff --git a/app/code/Magento/Catalog/etc/module.xml b/app/code/Magento/Catalog/etc/module.xml
index 87e82543fc65b..b33181d063f6a 100644
--- a/app/code/Magento/Catalog/etc/module.xml
+++ b/app/code/Magento/Catalog/etc/module.xml
@@ -6,7 +6,7 @@
*/
-->
-
+
diff --git a/app/code/Magento/Msrp/Setup/UpgradeData.php b/app/code/Magento/Msrp/Setup/UpgradeData.php
new file mode 100644
index 0000000000000..fef6dc192f97e
--- /dev/null
+++ b/app/code/Magento/Msrp/Setup/UpgradeData.php
@@ -0,0 +1,65 @@
+categorySetupFactory = $categorySetupFactory;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
+ {
+ /** @var \Magento\Catalog\Setup\CategorySetup $categorySetup */
+ $categorySetup = $this->categorySetupFactory->create(['setup' => $setup]);
+ $entityTypeId = $categorySetup->getEntityTypeId(\Magento\Catalog\Model\Product::ENTITY);
+
+ if (version_compare($context->getVersion(), '2.1.3', '<')) {
+ $this->changePriceAttributeDefaultScope($categorySetup, $entityTypeId);
+ }
+ $setup->endSetup();
+ }
+
+ /**
+ * @param \Magento\Catalog\Setup\CategorySetup $categorySetup
+ * @param int $entityTypeId
+ * @return void
+ */
+ private function changePriceAttributeDefaultScope($categorySetup, $entityTypeId)
+ {
+ $attribute = $categorySetup->getAttribute($entityTypeId, 'msrp');
+ $categorySetup->updateAttribute(
+ $entityTypeId,
+ $attribute['attribute_id'],
+ 'is_global',
+ \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL
+ );
+
+ }
+}
diff --git a/app/code/Magento/Msrp/etc/module.xml b/app/code/Magento/Msrp/etc/module.xml
index 16265db3fcbb4..1232c3422bd5f 100644
--- a/app/code/Magento/Msrp/etc/module.xml
+++ b/app/code/Magento/Msrp/etc/module.xml
@@ -6,7 +6,7 @@
*/
-->
-
+
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Cron/DeleteOutdatedPriceValuesTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Cron/DeleteOutdatedPriceValuesTest.php
new file mode 100644
index 0000000000000..de4129ddfaa5e
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Cron/DeleteOutdatedPriceValuesTest.php
@@ -0,0 +1,95 @@
+objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
+ $this->store = $this->objectManager->create(\Magento\Store\Model\Store::class);
+ $this->cron = $this->objectManager->create(\Magento\Catalog\Cron\DeleteOutdatedPriceValues::class);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple.php
+ * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
+ * @magentoConfigFixture current_store catalog/price/scope 2
+ * @magentoDbIsolation enabled
+ */
+ public function testExecute()
+ {
+ $secondStoreId = $this->store->load('fixture_second_store')->getId();
+ /** @var \Magento\Catalog\Model\Product\Action $productAction */
+ $productAction = $this->objectManager->create(
+ \Magento\Catalog\Model\Product\Action::class
+ );
+
+ $product = $this->productRepository->get('simple');
+ $productResource = $this->objectManager->create(\Magento\Catalog\Model\ResourceModel\Product::class);
+
+ $productId = $product->getId();
+ $productAction->updateWebsites(
+ [$productId],
+ [$this->store->load('fixture_second_store')->getWebsiteId()],
+ 'add'
+ );
+ $product->setOrigData();
+ $product->setStoreId($secondStoreId);
+ $product->setPrice(9.99);
+
+ $productResource->save($product);
+ $attribute = $this->objectManager->get(\Magento\Eav\Model\Config::class)
+ ->getAttribute(
+ 'catalog_product',
+ 'price'
+ );
+ $this->assertEquals(
+ '9.99',
+ $productResource->getAttributeRawValue($productId, $attribute->getId(), $secondStoreId)
+ );
+ /** @var MutableScopeConfigInterface $config */
+ $config = $this->objectManager->get(
+ MutableScopeConfigInterface::class
+ );
+ $config->setValue(
+ \Magento\Store\Model\Store::XML_PATH_PRICE_SCOPE,
+ \Magento\Store\Model\Store::PRICE_SCOPE_GLOBAL,
+ ScopeConfigInterface::SCOPE_TYPE_DEFAULT
+ );
+ /** @var \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig */
+ $this->cron->execute();
+ $this->assertEquals(
+ '10.0000',
+ $productResource->getAttributeRawValue($productId, $attribute->getId(), $secondStoreId)
+ );
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/PriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/PriceTest.php
index fb746835d7b38..1153a92ff6ea9 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/PriceTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Backend/PriceTest.php
@@ -8,23 +8,30 @@
/**
* Test class for \Magento\Catalog\Model\Product\Attribute\Backend\Price.
*
- * @magentoDataFixture Magento/Catalog/_files/product_simple.php
+ * @magentoDbIsolation enabled
+ * @magentoAppArea adminhtml
*/
class PriceTest extends \PHPUnit_Framework_TestCase
{
/**
* @var \Magento\Catalog\Model\Product\Attribute\Backend\Price
*/
- protected $_model;
+ private $model;
+
+ /**
+ * @var \Magento\TestFramework\ObjectManager
+ */
+ private $objectManager;
protected function setUp()
{
- $this->_model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- 'Magento\Catalog\Model\Product\Attribute\Backend\Price'
+ $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $this->model = $this->objectManager->create(
+ \Magento\Catalog\Model\Product\Attribute\Backend\Price::class
);
- $this->_model->setAttribute(
- \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
- 'Magento\Eav\Model\Config'
+ $this->model->setAttribute(
+ $this->objectManager->get(
+ \Magento\Eav\Model\Config::class
)->getAttribute(
'catalog_product',
'price'
@@ -37,50 +44,211 @@ public function testSetScopeDefault()
/* validate result of setAttribute */
$this->assertEquals(
\Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL,
- $this->_model->getAttribute()->getIsGlobal()
+ $this->model->getAttribute()->getIsGlobal()
);
- $this->_model->setScope($this->_model->getAttribute());
+ $this->model->setScope($this->model->getAttribute());
$this->assertEquals(
\Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL,
- $this->_model->getAttribute()->getIsGlobal()
+ $this->model->getAttribute()->getIsGlobal()
);
}
/**
+ * @magentoDbIsolation enabled
* @magentoConfigFixture current_store catalog/price/scope 1
*/
public function testSetScope()
{
- $this->_model->setScope($this->_model->getAttribute());
+ $this->model->setScope($this->model->getAttribute());
$this->assertEquals(
\Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE,
- $this->_model->getAttribute()->getIsGlobal()
+ $this->model->getAttribute()->getIsGlobal()
);
}
/**
+ * @magentoDbIsolation enabled
+ * @magentoDataFixture Magento/Catalog/_files/product_simple.php
* @magentoConfigFixture current_store catalog/price/scope 1
* @magentoConfigFixture current_store currency/options/base GBP
*/
public function testAfterSave()
{
- $repository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- 'Magento\Catalog\Model\ProductRepository'
+ $repository = $this->objectManager->create(
+ \Magento\Catalog\Model\ProductRepository::class
);
$product = $repository->get('simple');
$product->setOrigData();
$product->setPrice(9.99);
$product->setStoreId(0);
- $product->save();
+ $repository->save($product);
$this->assertEquals(
'9.99',
$product->getResource()->getAttributeRawValue(
$product->getId(),
- $this->_model->getAttribute()->getId(),
- \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
- 'Magento\Store\Model\StoreManagerInterface'
+ $this->model->getAttribute()->getId(),
+ $this->objectManager->get(
+ \Magento\Store\Model\StoreManagerInterface::class
)->getStore()->getId()
)
);
}
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple.php
+ * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
+ * @magentoConfigFixture current_store catalog/price/scope 2
+ * @magentoDbIsolation enabled
+ * @magentoAppArea adminhtml
+ */
+ public function testAfterSaveWithDifferentStores()
+ {
+ $repository = $this->objectManager->create(
+ \Magento\Catalog\Model\ProductRepository::class
+ );
+ /** @var \Magento\Store\Model\Store $store */
+ $store = $this->objectManager->create(
+ \Magento\Store\Model\Store::class
+ );
+ $globalStoreId = $store->load('admin')->getId();
+ $secondStoreId = $store->load('fixture_second_store')->getId();
+ $thirdStoreId = $store->load('fixture_third_store')->getId();
+ /** @var \Magento\Catalog\Model\Product\Action $productAction */
+ $productAction = $this->objectManager->create(
+ \Magento\Catalog\Model\Product\Action::class
+ );
+
+ $product = $repository->get('simple');
+ $productId = $product->getId();
+ $productResource = $product->getResource();
+ $productAction->updateWebsites([$productId], [$store->load('fixture_second_store')->getWebsiteId()], 'add');
+ $product->setOrigData();
+ $product->setStoreId($secondStoreId);
+ $product->setPrice(9.99);
+ $productResource->save($product);
+
+ $this->assertEquals(
+ '10.00',
+ $productResource->getAttributeRawValue($productId, $this->model->getAttribute()->getId(), $globalStoreId)
+ );
+ $this->assertEquals(
+ '9.99',
+ $productResource->getAttributeRawValue($productId, $this->model->getAttribute()->getId(), $secondStoreId)
+ );
+ $this->assertEquals(
+ '9.99',
+ $productResource->getAttributeRawValue($productId, $this->model->getAttribute()->getId(), $thirdStoreId)
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple.php
+ * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
+ * @magentoConfigFixture current_store catalog/price/scope 2
+ * @magentoDbIsolation enabled
+ * @magentoAppArea adminhtml
+ */
+ public function testAfterSaveWithSameCurrency()
+ {
+ $repository = $this->objectManager->create(
+ \Magento\Catalog\Model\ProductRepository::class
+ );
+ /** @var \Magento\Store\Model\Store $store */
+ $store = $this->objectManager->create(
+ \Magento\Store\Model\Store::class
+ );
+ $globalStoreId = $store->load('admin')->getId();
+ $secondStoreId = $store->load('fixture_second_store')->getId();
+ $thirdStoreId = $store->load('fixture_third_store')->getId();
+ /** @var \Magento\Catalog\Model\Product\Action $productAction */
+ $productAction = $this->objectManager->create(
+ \Magento\Catalog\Model\Product\Action::class
+ );
+
+ $product = $repository->get('simple');
+ $productId = $product->getId();
+ $productResource = $product->getResource();
+ $productAction->updateWebsites([$productId], [$store->load('fixture_second_store')->getWebsiteId()], 'add');
+ $product->setOrigData();
+ $product->setStoreId($secondStoreId);
+ $product->setPrice(9.99);
+ $productResource->save($product);
+
+ $this->assertEquals(
+ '10.00',
+ $productResource->getAttributeRawValue($productId, $this->model->getAttribute()->getId(), $globalStoreId)
+ );
+ $this->assertEquals(
+ '9.99',
+ $productResource->getAttributeRawValue($productId, $this->model->getAttribute()->getId(), $secondStoreId)
+ );
+ $this->assertEquals(
+ '9.99',
+ $productResource->getAttributeRawValue($productId, $this->model->getAttribute()->getId(), $thirdStoreId)
+ );
+ }
+
+ /**
+ * @magentoDbIsolation enabled
+ * @magentoAppArea adminhtml
+ * @magentoDataFixture Magento/Catalog/_files/product_simple.php
+ * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
+ * @magentoConfigFixture current_store catalog/price/scope 2
+ */
+ public function testAfterSaveWithUseDefault()
+ {
+ $repository = $this->objectManager->create(
+ \Magento\Catalog\Model\ProductRepository::class
+ );
+ /** @var \Magento\Store\Model\Store $store */
+ $store = $this->objectManager->create(
+ \Magento\Store\Model\Store::class
+ );
+ $globalStoreId = $store->load('admin')->getId();
+ $secondStoreId = $store->load('fixture_second_store')->getId();
+ $thirdStoreId = $store->load('fixture_third_store')->getId();
+ /** @var \Magento\Catalog\Model\Product\Action $productAction */
+ $productAction = $this->objectManager->create(
+ \Magento\Catalog\Model\Product\Action::class
+ );
+
+ $product = $repository->get('simple');
+ $productId = $product->getId();
+ $productResource = $product->getResource();
+ $productAction->updateWebsites([$productId], [$store->load('fixture_second_store')->getWebsiteId()], 'add');
+ $product->setOrigData();
+ $product->setStoreId($secondStoreId);
+ $product->setPrice(9.99);
+ $productResource->save($product);
+
+ $this->assertEquals(
+ '10.00',
+ $productResource->getAttributeRawValue($productId, $this->model->getAttribute()->getId(), $globalStoreId)
+ );
+ $this->assertEquals(
+ '9.99',
+ $productResource->getAttributeRawValue($productId, $this->model->getAttribute()->getId(), $secondStoreId)
+ );
+ $this->assertEquals(
+ '9.99',
+ $productResource->getAttributeRawValue($productId, $this->model->getAttribute()->getId(), $thirdStoreId)
+ );
+
+ $product->setStoreId($thirdStoreId);
+ $product->setPrice(null);
+ $productResource->save($product);
+
+ $this->assertEquals(
+ '10.00',
+ $productResource->getAttributeRawValue($productId, $this->model->getAttribute()->getId(), $globalStoreId)
+ );
+ $this->assertEquals(
+ '9.99',
+ $productResource->getAttributeRawValue($productId, $this->model->getAttribute()->getId(), $secondStoreId)
+ );
+ $this->assertEquals(
+ '10.00',
+ $productResource->getAttributeRawValue($productId, $this->model->getAttribute()->getId(), $thirdStoreId)
+ );
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Observer/SwitchPriceAttributeScopeOnConfigChangeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Observer/SwitchPriceAttributeScopeOnConfigChangeTest.php
new file mode 100644
index 0000000000000..0de309a8dcf99
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Observer/SwitchPriceAttributeScopeOnConfigChangeTest.php
@@ -0,0 +1,67 @@
+objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ }
+
+ /**
+ * @magentoDbIsolation enabled
+ * @magentoAppArea adminhtml
+ */
+ public function testPriceAttributeHasScopeGlobal()
+ {
+ foreach (['price', 'cost', 'special_price'] as $attributeCode) {
+ $attribute = $this->objectManager->get(\Magento\Eav\Model\Config::class)->getAttribute(
+ 'catalog_product',
+ $attributeCode
+ );
+ $this->assertTrue($attribute->isScopeGlobal());
+ }
+ }
+
+ /**
+ * @magentoDbIsolation enabled
+ * @magentoAppArea adminhtml
+ */
+ public function testPriceAttributeHasScopeWebsite()
+ {
+ /** @var ReinitableConfigInterface $config */
+ $config = $this->objectManager->get(
+ ReinitableConfigInterface::class
+ );
+ $config->setValue(
+ \Magento\Store\Model\Store::XML_PATH_PRICE_SCOPE,
+ \Magento\Store\Model\Store::PRICE_SCOPE_WEBSITE,
+ ScopeConfigInterface::SCOPE_TYPE_DEFAULT
+ );
+
+ $eventManager = $this->objectManager->get(\Magento\Framework\Event\ManagerInterface::class);
+ $eventManager->dispatch(
+ "admin_system_config_changed_section_catalog",
+ ['website' => 0, 'store' => 0]
+ );
+ foreach (['price', 'cost', 'special_price'] as $attributeCode) {
+ $attribute = $this->objectManager->get(\Magento\Eav\Model\Config::class)->getAttribute(
+ 'catalog_product',
+ $attributeCode
+ );
+ $this->assertTrue($attribute->isScopeWebsite());
+ }
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/second_website_with_two_stores.php b/dev/tests/integration/testsuite/Magento/Store/_files/second_website_with_two_stores.php
new file mode 100644
index 0000000000000..385b6452efba8
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Store/_files/second_website_with_two_stores.php
@@ -0,0 +1,63 @@
+create(\Magento\Store\Model\Website::class);
+/** @var $website \Magento\Store\Model\Website */
+if (!$website->load('test', 'code')->getId()) {
+ $website->setData(['code' => 'test', 'name' => 'Test Website', 'default_group_id' => '1', 'is_default' => '0']);
+ $website->save();
+}
+$websiteId = $website->getId();
+$store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class);
+if (!$store->load('fixture_second_store', 'code')->getId()) {
+ $groupId = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ \Magento\Store\Model\StoreManagerInterface::class
+ )->getWebsite()->getDefaultGroupId();
+ $store->setCode(
+ 'fixture_second_store'
+ )->setWebsiteId(
+ $websiteId
+ )->setGroupId(
+ $groupId
+ )->setName(
+ 'Fixture Second Store'
+ )->setSortOrder(
+ 10
+ )->setIsActive(
+ 1
+ );
+ $store->save();
+}
+
+$store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class);
+if (!$store->load('fixture_third_store', 'code')->getId()) {
+ $groupId = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ \Magento\Store\Model\StoreManagerInterface::class
+ )->getWebsite()->getDefaultGroupId();
+ $store->setCode(
+ 'fixture_third_store'
+ )->setWebsiteId(
+ $websiteId
+ )->setGroupId(
+ $groupId
+ )->setName(
+ 'Fixture Third Store'
+ )->setSortOrder(
+ 11
+ )->setIsActive(
+ 1
+ );
+ $store->save();
+}
+/* Refresh stores memory cache */
+\Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ \Magento\Store\Model\StoreManagerInterface::class
+)->reinitStores();
+
+/* Refresh CatalogSearch index */
+/** @var \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry */
+$indexerRegistry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ ->create(\Magento\Framework\Indexer\IndexerRegistry::class);
+$indexerRegistry->get(\Magento\CatalogSearch\Model\Indexer\Fulltext::INDEXER_ID)->reindexAll();
diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/second_website_with_two_stores_rollback.php b/dev/tests/integration/testsuite/Magento/Store/_files/second_website_with_two_stores_rollback.php
new file mode 100644
index 0000000000000..c6d03a1fe4556
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Store/_files/second_website_with_two_stores_rollback.php
@@ -0,0 +1,29 @@
+get(\Magento\Framework\Registry::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+$website = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Website::class);
+/** @var $website \Magento\Store\Model\Website */
+if ($website->load('test', 'code')->getId()) {
+ $website->delete();
+}
+$websiteId = $website->getId();
+$store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class);
+if ($store->load('fixture_second_store', 'code')->getId()) {
+ $store->delete();
+}
+
+$store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class);
+if ($store->load('fixture_third_store', 'code')->getId()) {
+ $store->delete();
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/lib/internal/Magento/Framework/App/ReinitableConfig.php b/lib/internal/Magento/Framework/App/ReinitableConfig.php
index b98ede77fe133..3b75d163ff154 100644
--- a/lib/internal/Magento/Framework/App/ReinitableConfig.php
+++ b/lib/internal/Magento/Framework/App/ReinitableConfig.php
@@ -18,6 +18,7 @@ class ReinitableConfig extends MutableScopeConfig implements ReinitableConfigInt
public function reinit()
{
$this->_scopePool->clean();
+ $this->clean();
return $this;
}
}