Skip to content

Commit b3a3a72

Browse files
author
Sergey Shvets
committed
Merge branch '2.1' of github.com:magento/magento2ce into MAGETWO-61039
2 parents 0e3b417 + 673217a commit b3a3a72

File tree

31 files changed

+1411
-307
lines changed

31 files changed

+1411
-307
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
/**
3+
* Copyright © 2016 Magento. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Catalog\Cron;
7+
8+
use Magento\Framework\App\ResourceConnection;
9+
use Magento\Eav\Api\AttributeRepositoryInterface as AttributeRepository;
10+
use Magento\Framework\App\Config\MutableScopeConfigInterface as ScopeConfig;
11+
use Magento\Catalog\Api\Data\ProductAttributeInterface;
12+
use Magento\Store\Model\Store;
13+
14+
/**
15+
* Cron operation is responsible for deleting all product prices on WEBSITE level
16+
* in case 'Catalog Price Scope' configuratoin parameter is set to GLOBAL.
17+
*/
18+
class DeleteOutdatedPriceValues
19+
{
20+
/**
21+
* @var ResourceConnection
22+
*/
23+
private $resource;
24+
25+
/**
26+
* @var AttributeRepository
27+
*/
28+
private $attributeRepository;
29+
30+
/**
31+
* @var ScopeConfig
32+
*/
33+
private $scopeConfig;
34+
35+
/**
36+
* @param ResourceConnection $resource
37+
* @param AttributeRepository $attributeRepository
38+
* @param ScopeConfig $scopeConfig
39+
*/
40+
public function __construct(
41+
ResourceConnection $resource,
42+
AttributeRepository $attributeRepository,
43+
ScopeConfig $scopeConfig
44+
) {
45+
$this->resource = $resource;
46+
$this->attributeRepository = $attributeRepository;
47+
$this->scopeConfig = $scopeConfig;
48+
}
49+
50+
/**
51+
* Delete all price values for non-admin stores if PRICE_SCOPE is global
52+
*
53+
* @return void
54+
*/
55+
public function execute()
56+
{
57+
$priceScope = $this->scopeConfig->getValue(Store::XML_PATH_PRICE_SCOPE);
58+
if ($priceScope == Store::PRICE_SCOPE_GLOBAL) {
59+
/** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $priceAttribute */
60+
$priceAttribute = $this->attributeRepository
61+
->get(ProductAttributeInterface::ENTITY_TYPE_CODE, ProductAttributeInterface::CODE_PRICE);
62+
$connection = $this->resource->getConnection();
63+
$conditions = [
64+
$connection->quoteInto('attribute_id = ?', $priceAttribute->getId()),
65+
$connection->quoteInto('store_id != ?', Store::DEFAULT_STORE_ID),
66+
];
67+
68+
$connection->delete(
69+
$priceAttribute->getBackend()->getTable(),
70+
$conditions
71+
);
72+
}
73+
}
74+
}

app/code/Magento/Catalog/Model/Product/Attribute/Backend/Price.php

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66
namespace Magento\Catalog\Model\Product\Attribute\Backend;
77

8-
use \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
8+
use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
99

1010
/**
1111
* Catalog product price attribute backend model
@@ -102,43 +102,27 @@ public function setScope($attribute)
102102
}
103103

104104
/**
105-
* After Save Attribute manipulation
105+
* After Save Price Attribute manipulation
106+
* Processes product price attributes if price scoped to website and updates data when:
107+
* * Price changed for non-default store view - will update price for all stores assigned to current website.
108+
* * Price will be changed according to store currency even if price changed in product with default store id.
109+
* * In a case when price was removed for non-default store (use default option checked) the default store price
110+
* * will be used instead
106111
*
107112
* @param \Magento\Catalog\Model\Product $object
108113
* @return $this
109-
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
110114
*/
111115
public function afterSave($object)
112116
{
113-
$value = $object->getData($this->getAttribute()->getAttributeCode());
114-
/**
115-
* Orig value is only for existing objects
116-
*/
117-
$oridData = $object->getOrigData();
118-
$origValueExist = $oridData && array_key_exists($this->getAttribute()->getAttributeCode(), $oridData);
119-
if ($object->getStoreId() != 0 || !$value || $origValueExist) {
120-
return $this;
121-
}
122-
123-
if ($this->getAttribute()->getIsGlobal() == ScopedAttributeInterface::SCOPE_WEBSITE) {
124-
$baseCurrency = $this->_config->getValue(
125-
\Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE,
126-
'default'
127-
);
128-
129-
$storeIds = $object->getStoreIds();
130-
if (is_array($storeIds)) {
131-
foreach ($storeIds as $storeId) {
132-
$storeCurrency = $this->_storeManager->getStore($storeId)->getBaseCurrencyCode();
133-
if ($storeCurrency == $baseCurrency) {
134-
continue;
135-
}
136-
$rate = $this->_currencyFactory->create()->load($baseCurrency)->getRate($storeCurrency);
137-
if (!$rate) {
138-
$rate = 1;
139-
}
140-
$newValue = $value * $rate;
141-
$object->addAttributeUpdate($this->getAttribute()->getAttributeCode(), $newValue, $storeId);
117+
/** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
118+
$attribute = $this->getAttribute();
119+
$attributeCode = $attribute->getAttributeCode();
120+
$value = $object->getData($attributeCode);
121+
if ($value && $value != $object->getOrigData($attributeCode)) {
122+
if ($attribute->isScopeWebsite()) {
123+
foreach ((array)$object->getWebsiteStoreIds() as $storeId) {
124+
/** @var $object \Magento\Catalog\Model\Product */
125+
$object->addAttributeUpdate($attributeCode, $value, $storeId);
142126
}
143127
}
144128
}

app/code/Magento/Catalog/Model/Product/Attribute/Repository.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,6 @@ public function save(\Magento\Catalog\Api\Data\ProductAttributeInterface $attrib
128128
}
129129
$attribute->setDefaultFrontendLabel($frontendLabel);
130130
}
131-
if (!$attribute->getIsUserDefined()) {
132-
// Unset attribute field for system attributes
133-
$attribute->setApplyTo(null);
134-
}
135131
} else {
136132
$attribute->setAttributeId(null);
137133

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
/**
3+
* Copyright © 2016 Magento. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Catalog\Model\Product\Pricing\Renderer;
8+
9+
/**
10+
* Resolver provided to check is product available for sale
11+
*/
12+
class SalableResolver implements SalableResolverInterface
13+
{
14+
/**
15+
* Check is product available for sale
16+
*
17+
* @param \Magento\Framework\Pricing\SaleableInterface $salableItem
18+
* @return boolean
19+
*/
20+
public function isSalable(\Magento\Framework\Pricing\SaleableInterface $salableItem)
21+
{
22+
return $salableItem->getCanShowPrice() !== false && $salableItem->isSalable();
23+
}
24+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
/**
3+
* Copyright © 2016 Magento. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Catalog\Model\Product\Pricing\Renderer;
8+
9+
/**
10+
* Interface resolver provided to check is product available for sale
11+
*/
12+
interface SalableResolverInterface
13+
{
14+
/**
15+
* Check is product available for sale
16+
*
17+
* @param \Magento\Framework\Pricing\SaleableInterface $salableItem
18+
* @return boolean
19+
*/
20+
public function isSalable(\Magento\Framework\Pricing\SaleableInterface $salableItem);
21+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
/**
3+
* Copyright © 2016 Magento. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Catalog\Observer;
7+
8+
use Magento\Framework\Event\Observer as EventObserver;
9+
use Magento\Framework\Event\ObserverInterface;
10+
use Magento\Catalog\Api\Data\ProductAttributeInterface;
11+
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
12+
use Magento\Store\Model\Store;
13+
use Magento\Framework\App\Config\ReinitableConfigInterface;
14+
use Magento\Framework\Api\SearchCriteriaBuilder;
15+
16+
/**
17+
* Observer is responsible for changing scope for all price attributes in system
18+
* depending on 'Catalog Price Scope' configuration parameter
19+
*/
20+
class SwitchPriceAttributeScopeOnConfigChange implements ObserverInterface
21+
{
22+
/**
23+
* @var ReinitableConfigInterface
24+
*/
25+
private $config;
26+
27+
/**
28+
* @var ProductAttributeRepositoryInterface
29+
*/
30+
private $productAttributeRepository;
31+
32+
/**
33+
* @var SearchCriteriaBuilder
34+
*/
35+
private $searchCriteriaBuilder;
36+
37+
/**
38+
* @param ReinitableConfigInterface $config
39+
* @param ProductAttributeRepositoryInterface $productAttributeRepository
40+
* @param SearchCriteriaBuilder $searchCriteriaBuilder
41+
*/
42+
public function __construct(
43+
ReinitableConfigInterface $config,
44+
ProductAttributeRepositoryInterface $productAttributeRepository,
45+
SearchCriteriaBuilder $searchCriteriaBuilder
46+
){
47+
$this->config = $config;
48+
$this->productAttributeRepository = $productAttributeRepository;
49+
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
50+
}
51+
52+
/**
53+
* Change scope for all price attributes according to
54+
* 'Catalog Price Scope' configuration parameter value
55+
*
56+
* @param EventObserver $observer
57+
* @return void
58+
*/
59+
public function execute(EventObserver $observer)
60+
{
61+
$this->searchCriteriaBuilder->addFilter('frontend_input', 'price');
62+
$criteria = $this->searchCriteriaBuilder->create();
63+
64+
$scope = $this->config->getValue(Store::XML_PATH_PRICE_SCOPE);
65+
$scope = ($scope == Store::PRICE_SCOPE_WEBSITE)
66+
? ProductAttributeInterface::SCOPE_WEBSITE_TEXT
67+
: ProductAttributeInterface::SCOPE_GLOBAL_TEXT;
68+
69+
$priceAttributes = $this->productAttributeRepository->getList($criteria)->getItems();
70+
71+
/** @var ProductAttributeInterface $priceAttribute */
72+
foreach ($priceAttributes as $priceAttribute) {
73+
$priceAttribute->setScope($scope);
74+
$this->productAttributeRepository->save($priceAttribute);
75+
}
76+
}
77+
}

app/code/Magento/Catalog/Pricing/Render/FinalPriceBox.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
use Magento\Framework\Pricing\Render;
1111
use Magento\Framework\Pricing\Render\PriceBox as BasePriceBox;
1212
use Magento\Msrp\Pricing\Price\MsrpPrice;
13+
use Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolverInterface;
14+
use Magento\Framework\View\Element\Template\Context;
15+
use Magento\Framework\Pricing\SaleableInterface;
16+
use Magento\Framework\Pricing\Price\PriceInterface;
17+
use Magento\Framework\Pricing\Render\RendererPool;
1318

1419
/**
1520
* Class for final_price rendering
@@ -19,12 +24,38 @@
1924
*/
2025
class FinalPriceBox extends BasePriceBox
2126
{
27+
/**
28+
* @var SalableResolverInterface
29+
*/
30+
private $salableResolver;
31+
32+
/**
33+
* @param Context $context
34+
* @param SaleableInterface $saleableItem
35+
* @param PriceInterface $price
36+
* @param RendererPool $rendererPool
37+
* @param array $data
38+
* @param SalableResolverInterface $salableResolver
39+
*/
40+
public function __construct(
41+
Context $context,
42+
SaleableInterface $saleableItem,
43+
PriceInterface $price,
44+
RendererPool $rendererPool,
45+
array $data = [],
46+
SalableResolverInterface $salableResolver = null
47+
) {
48+
parent::__construct($context, $saleableItem, $price, $rendererPool, $data);
49+
$this->salableResolver = $salableResolver ?: \Magento\Framework\App\ObjectManager::getInstance()
50+
->get(SalableResolverInterface::class);
51+
}
52+
2253
/**
2354
* @return string
2455
*/
2556
protected function _toHtml()
2657
{
27-
if (!$this->getSaleableItem() || $this->getSaleableItem()->getCanShowPrice() === false) {
58+
if (!$this->salableResolver->isSalable($this->getSaleableItem())) {
2859
return '';
2960
}
3061

app/code/Magento/Catalog/Setup/UpgradeData.php

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface
340340
}
341341

342342
if (version_compare($context->getVersion(), '2.0.7') < 0) {
343-
/** @var EavSetup $eavSetupF */
343+
/** @var EavSetup $eavSetup */
344344
$eavSetup= $this->eavSetupFactory->create(['setup' => $setup]);
345345

346346
$eavSetup->updateAttribute(
@@ -351,7 +351,32 @@ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface
351351
]
352352
);
353353
}
354-
354+
355+
if (version_compare($context->getVersion(), '2.1.3') < 0) {
356+
/** @var \Magento\Catalog\Setup\CategorySetup $categorySetup */
357+
$categorySetup = $this->categorySetupFactory->create(['setup' => $setup]);
358+
$this->changePriceAttributeDefaultScope($categorySetup);
359+
}
360+
355361
$setup->endSetup();
356362
}
363+
364+
/**
365+
* @param \Magento\Catalog\Setup\CategorySetup $categorySetup
366+
* @return void
367+
*/
368+
private function changePriceAttributeDefaultScope($categorySetup)
369+
{
370+
$entityTypeId = $categorySetup->getEntityTypeId(\Magento\Catalog\Model\Product::ENTITY);
371+
foreach (['price', 'cost', 'special_price'] as $attributeCode) {
372+
$attribute = $categorySetup->getAttribute($entityTypeId, $attributeCode);
373+
$categorySetup->updateAttribute(
374+
$entityTypeId,
375+
$attribute['attribute_id'],
376+
'is_global',
377+
\Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL
378+
);
379+
380+
}
381+
}
357382
}

0 commit comments

Comments
 (0)