Skip to content

Commit ddf58d1

Browse files
committed
MAGETWO-83706: Scheduled Update to existing Group Price / Special Price removes the previously configured price, or results in changes not being saved
1 parent eb1970a commit ddf58d1

File tree

5 files changed

+514
-140
lines changed

5 files changed

+514
-140
lines changed

app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ public function execute($entity, $arguments = [])
7777
$priceRows = $entity->getData($attribute->getName());
7878
if (null !== $priceRows) {
7979
if (!is_array($priceRows)) {
80-
throw new \Magento\Framework\Exception\LocalizedException(
81-
__('Something went wrong while processing the request.')
80+
throw new \Magento\Framework\Exception\RuntimeException(
81+
__('Tier prices data should be array, but actually other type is received')
8282
);
8383
}
8484
$websiteId = $this->storeManager->getStore($entity->getStoreId())->getWebsiteId();
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Catalog\Test\Unit\Model\Attribute\Backend\TierPrice;
8+
9+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
10+
use Magento\Catalog\Model\Product\Attribute\Backend\TierPrice\SaveHandler;
11+
use Magento\Store\Model\StoreManagerInterface;
12+
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
13+
use Magento\Customer\Api\GroupManagementInterface;
14+
use Magento\Framework\EntityManager\MetadataPool;
15+
use Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice;
16+
17+
/**
18+
* Unit tests for \Magento\Catalog\Model\Product\Attribute\Backend\TierPrice\SaveHandler
19+
*/
20+
class SaveHandlerTest extends \PHPUnit\Framework\TestCase
21+
{
22+
/**
23+
* Magento\Framework\TestFramework\Unit\Helper\ObjectManager
24+
*/
25+
private $objectManager;
26+
27+
/**
28+
* @var SaveHandler|\PHPUnit_Framework_MockObject_MockObject
29+
*/
30+
private $saveHandler;
31+
32+
/**
33+
* @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
34+
*/
35+
private $storeManager;
36+
37+
/**
38+
* @var ProductAttributeRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
39+
*/
40+
private $attributeRepository;
41+
42+
/**
43+
* @var GroupManagementInterface|\PHPUnit_Framework_MockObject_MockObject
44+
*/
45+
private $groupManagement;
46+
47+
/**
48+
* @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject
49+
*/
50+
private $metadataPoll;
51+
52+
/**
53+
* @var Tierprice|\PHPUnit_Framework_MockObject_MockObject
54+
*/
55+
private $tierPriceResource;
56+
57+
/**
58+
* @inheritdoc
59+
*/
60+
protected function setUp()
61+
{
62+
$this->objectManager = new ObjectManager($this);
63+
$this->storeManager = $this->getMockBuilder(StoreManagerInterface::class)
64+
->disableOriginalConstructor()
65+
->setMethods(['getStore'])
66+
->getMockForAbstractClass();
67+
$this->attributeRepository = $this->getMockBuilder(ProductAttributeRepositoryInterface::class)
68+
->disableOriginalConstructor()
69+
->setMethods(['get'])
70+
->getMockForAbstractClass();
71+
$this->groupManagement = $this->getMockBuilder(GroupManagementInterface::class)
72+
->disableOriginalConstructor()
73+
->setMethods(['getAllCustomersGroup'])
74+
->getMockForAbstractClass();
75+
$this->metadataPoll = $this->getMockBuilder(MetadataPool::class)
76+
->disableOriginalConstructor()
77+
->setMethods(['getMetadata'])
78+
->getMock();
79+
$this->tierPriceResource = $this->getMockBuilder(Tierprice::class)
80+
->disableOriginalConstructor()
81+
->setMethods([])
82+
->getMock();
83+
84+
$this->saveHandler = $this->objectManager->getObject(
85+
\Magento\Catalog\Model\Product\Attribute\Backend\TierPrice\SaveHandler::class,
86+
[
87+
'storeManager' => $this->storeManager,
88+
'attributeRepository' => $this->attributeRepository,
89+
'groupManagement' => $this->groupManagement,
90+
'metadataPoll' => $this->metadataPoll,
91+
'tierPriceResource' => $this->tierPriceResource
92+
]
93+
);
94+
}
95+
96+
public function testExecute()
97+
{
98+
$tierPrices = [
99+
['website_id' => 0, 'price_qty' => 2, 'cust_group' => 0, 'price' => 10],
100+
['website_id' => 0, 'price_qty' => 3, 'cust_group' => 3200, 'price' => null, 'percentage_value' => 20]
101+
];
102+
$linkField = 'entity_id';
103+
$productId = 10;
104+
105+
/** @var \PHPUnit_Framework_MockObject_MockObject $product */
106+
$product = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class)
107+
->disableOriginalConstructor()
108+
->setMethods(['getData','setData', 'getStoreId'])
109+
->getMockForAbstractClass();
110+
$product->expects($this->atLeastOnce())->method('getData')->willReturnMap(
111+
[
112+
['tier_price', $tierPrices],
113+
['entity_id', $productId]
114+
]
115+
);
116+
$product->expects($this->atLeastOnce())->method('getStoreId')->willReturn(0);
117+
$product->expects($this->atLeastOnce())->method('setData')->with('tier_price_changed', 1);
118+
$store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class)
119+
->disableOriginalConstructor()
120+
->setMethods(['getWebsiteId'])
121+
->getMockForAbstractClass();
122+
$store->expects($this->atLeastOnce())->method('getWebsiteId')->willReturn(0);
123+
$this->storeManager->expects($this->atLeastOnce())->method('getStore')->willReturn($store);
124+
/** @var \PHPUnit_Framework_MockObject_MockObject $attribute */
125+
$attribute = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductAttributeInterface::class)
126+
->disableOriginalConstructor()
127+
->setMethods(['getName', 'isScopeGlobal'])
128+
->getMockForAbstractClass();
129+
$attribute->expects($this->atLeastOnce())->method('getName')->willReturn('tier_price');
130+
$attribute->expects($this->atLeastOnce())->method('isScopeGlobal')->willReturn(true);
131+
$this->attributeRepository->expects($this->atLeastOnce())->method('get')->with('tier_price')
132+
->willReturn($attribute);
133+
$productMetadata = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadataInterface::class)
134+
->disableOriginalConstructor()
135+
->setMethods(['getLinkField'])
136+
->getMockForAbstractClass();
137+
$productMetadata->expects($this->atLeastOnce())->method('getLinkField')->willReturn($linkField);
138+
$this->metadataPoll->expects($this->atLeastOnce())->method('getMetadata')
139+
->with(\Magento\Catalog\Api\Data\ProductInterface::class)
140+
->willReturn($productMetadata);
141+
$customerGroup = $this->getMockBuilder(\Magento\Customer\Api\Data\GroupInterface::class)
142+
->disableOriginalConstructor()
143+
->setMethods(['getId'])
144+
->getMockForAbstractClass();
145+
$customerGroup->expects($this->atLeastOnce())->method('getId')->willReturn(3200);
146+
$this->groupManagement->expects($this->atLeastOnce())->method('getAllCustomersGroup')
147+
->willReturn($customerGroup);
148+
$this->tierPriceResource->expects($this->atLeastOnce())->method('savePriceData')->willReturnSelf();
149+
150+
$this->assertEquals($product, $this->saveHandler->execute($product));
151+
}
152+
153+
/**
154+
* @expectedException \Magento\Framework\Exception\RuntimeException
155+
* @expectedExceptionMessage Tier prices data should be array, but actually other type is received
156+
*/
157+
public function testExecuteWithException()
158+
{
159+
/** @var \PHPUnit_Framework_MockObject_MockObject $attribute */
160+
$attribute = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductAttributeInterface::class)
161+
->disableOriginalConstructor()
162+
->setMethods(['getName', 'isScopeGlobal'])
163+
->getMockForAbstractClass();
164+
$attribute->expects($this->atLeastOnce())->method('getName')->willReturn('tier_price');
165+
$this->attributeRepository->expects($this->atLeastOnce())->method('get')->with('tier_price')
166+
->willReturn($attribute);
167+
/** @var \PHPUnit_Framework_MockObject_MockObject $product */
168+
$product = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class)
169+
->disableOriginalConstructor()
170+
->setMethods(['getData','setData', 'getStoreId', 'getOrigData'])
171+
->getMockForAbstractClass();
172+
$product->expects($this->atLeastOnce())->method('getData')->with('tier_price')->willReturn(1);
173+
174+
$this->saveHandler->execute($product);
175+
}
176+
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Catalog\Test\Unit\Model\Attribute\Backend\TierPrice;
8+
9+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
10+
use Magento\Catalog\Model\Product\Attribute\Backend\TierPrice\UpdateHandler;
11+
use Magento\Store\Model\StoreManagerInterface;
12+
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
13+
use Magento\Customer\Api\GroupManagementInterface;
14+
use Magento\Framework\EntityManager\MetadataPool;
15+
use Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice;
16+
17+
/**
18+
* Unit tests for \Magento\Catalog\Model\Product\Attribute\Backend\TierPrice\UpdateHandler
19+
*/
20+
class UpdateHandlerTest extends \PHPUnit\Framework\TestCase
21+
{
22+
/**
23+
* Magento\Framework\TestFramework\Unit\Helper\ObjectManager
24+
*/
25+
private $objectManager;
26+
27+
/**
28+
* @var UpdateHandler|\PHPUnit_Framework_MockObject_MockObject
29+
*/
30+
private $updateHandler;
31+
32+
/**
33+
* @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
34+
*/
35+
private $storeManager;
36+
37+
/**
38+
* @var ProductAttributeRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
39+
*/
40+
private $attributeRepository;
41+
42+
/**
43+
* @var GroupManagementInterface|\PHPUnit_Framework_MockObject_MockObject
44+
*/
45+
private $groupManagement;
46+
47+
/**
48+
* @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject
49+
*/
50+
private $metadataPoll;
51+
52+
/**
53+
* @var Tierprice|\PHPUnit_Framework_MockObject_MockObject
54+
*/
55+
private $tierPriceResource;
56+
57+
/**
58+
* @inheritdoc
59+
*/
60+
protected function setUp()
61+
{
62+
$this->objectManager = new ObjectManager($this);
63+
$this->storeManager = $this->getMockBuilder(StoreManagerInterface::class)
64+
->disableOriginalConstructor()
65+
->setMethods(['getStore'])
66+
->getMockForAbstractClass();
67+
$this->attributeRepository = $this->getMockBuilder(ProductAttributeRepositoryInterface::class)
68+
->disableOriginalConstructor()
69+
->setMethods(['get'])
70+
->getMockForAbstractClass();
71+
$this->groupManagement = $this->getMockBuilder(GroupManagementInterface::class)
72+
->disableOriginalConstructor()
73+
->setMethods(['getAllCustomersGroup'])
74+
->getMockForAbstractClass();
75+
$this->metadataPoll = $this->getMockBuilder(MetadataPool::class)
76+
->disableOriginalConstructor()
77+
->setMethods(['getMetadata'])
78+
->getMock();
79+
$this->tierPriceResource = $this->getMockBuilder(Tierprice::class)
80+
->disableOriginalConstructor()
81+
->setMethods([])
82+
->getMock();
83+
84+
$this->updateHandler = $this->objectManager->getObject(
85+
\Magento\Catalog\Model\Product\Attribute\Backend\TierPrice\UpdateHandler::class,
86+
[
87+
'storeManager' => $this->storeManager,
88+
'attributeRepository' => $this->attributeRepository,
89+
'groupManagement' => $this->groupManagement,
90+
'metadataPoll' => $this->metadataPoll,
91+
'tierPriceResource' => $this->tierPriceResource
92+
]
93+
);
94+
}
95+
96+
public function testExecute()
97+
{
98+
$newTierPrices = [
99+
['website_id' => 0, 'price_qty' => 2, 'cust_group' => 0, 'price' => 15],
100+
['website_id' => 0, 'price_qty' => 3, 'cust_group' => 3200, 'price' => null, 'percentage_value' => 20]
101+
];
102+
$priceIdToDelete = 2;
103+
$originalTierPrices = [
104+
['price_id' => 1, 'website_id' => 0, 'price_qty' => 2, 'cust_group' => 0, 'price' => 10],
105+
['price_id' => $priceIdToDelete, 'website_id' => 0, 'price_qty' => 4, 'cust_group' => 0, 'price' => 20],
106+
];
107+
$linkField = 'entity_id';
108+
$productId = 10;
109+
110+
/** @var \PHPUnit_Framework_MockObject_MockObject $product */
111+
$product = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class)
112+
->disableOriginalConstructor()
113+
->setMethods(['getData','setData', 'getStoreId', 'getOrigData'])
114+
->getMockForAbstractClass();
115+
$product->expects($this->atLeastOnce())->method('getData')->willReturnMap(
116+
[
117+
['tier_price', $newTierPrices],
118+
['entity_id', $productId]
119+
]
120+
);
121+
$product->expects($this->atLeastOnce())->method('getOrigData')->with('tier_price')
122+
->willReturn($originalTierPrices);
123+
$product->expects($this->atLeastOnce())->method('getStoreId')->willReturn(0);
124+
$product->expects($this->atLeastOnce())->method('setData')->with('tier_price_changed', 1);
125+
$store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class)
126+
->disableOriginalConstructor()
127+
->setMethods(['getWebsiteId'])
128+
->getMockForAbstractClass();
129+
$store->expects($this->atLeastOnce())->method('getWebsiteId')->willReturn(0);
130+
$this->storeManager->expects($this->atLeastOnce())->method('getStore')->willReturn($store);
131+
/** @var \PHPUnit_Framework_MockObject_MockObject $attribute */
132+
$attribute = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductAttributeInterface::class)
133+
->disableOriginalConstructor()
134+
->setMethods(['getName', 'isScopeGlobal'])
135+
->getMockForAbstractClass();
136+
$attribute->expects($this->atLeastOnce())->method('getName')->willReturn('tier_price');
137+
$attribute->expects($this->atLeastOnce())->method('isScopeGlobal')->willReturn(true);
138+
$this->attributeRepository->expects($this->atLeastOnce())->method('get')->with('tier_price')
139+
->willReturn($attribute);
140+
$productMetadata = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadataInterface::class)
141+
->disableOriginalConstructor()
142+
->setMethods(['getLinkField'])
143+
->getMockForAbstractClass();
144+
$productMetadata->expects($this->atLeastOnce())->method('getLinkField')->willReturn($linkField);
145+
$this->metadataPoll->expects($this->atLeastOnce())->method('getMetadata')
146+
->with(\Magento\Catalog\Api\Data\ProductInterface::class)
147+
->willReturn($productMetadata);
148+
$customerGroup = $this->getMockBuilder(\Magento\Customer\Api\Data\GroupInterface::class)
149+
->disableOriginalConstructor()
150+
->setMethods(['getId'])
151+
->getMockForAbstractClass();
152+
$customerGroup->expects($this->atLeastOnce())->method('getId')->willReturn(3200);
153+
$this->groupManagement->expects($this->atLeastOnce())->method('getAllCustomersGroup')
154+
->willReturn($customerGroup);
155+
$this->tierPriceResource->expects($this->exactly(2))->method('savePriceData')->willReturnSelf();
156+
$this->tierPriceResource->expects($this->once())->method('deletePriceData')
157+
->with($productId, null, $priceIdToDelete);
158+
159+
$this->assertEquals($product, $this->updateHandler->execute($product));
160+
}
161+
162+
/**
163+
* @expectedException \Magento\Framework\Exception\RuntimeException
164+
* @expectedExceptionMessage Tier prices data should be array, but actually other type is received
165+
*/
166+
public function testExecuteWithException()
167+
{
168+
/** @var \PHPUnit_Framework_MockObject_MockObject $attribute */
169+
$attribute = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductAttributeInterface::class)
170+
->disableOriginalConstructor()
171+
->setMethods(['getName', 'isScopeGlobal'])
172+
->getMockForAbstractClass();
173+
$attribute->expects($this->atLeastOnce())->method('getName')->willReturn('tier_price');
174+
$this->attributeRepository->expects($this->atLeastOnce())->method('get')->with('tier_price')
175+
->willReturn($attribute);
176+
/** @var \PHPUnit_Framework_MockObject_MockObject $product */
177+
$product = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class)
178+
->disableOriginalConstructor()
179+
->setMethods(['getData','setData', 'getStoreId', 'getOrigData'])
180+
->getMockForAbstractClass();
181+
$product->expects($this->atLeastOnce())->method('getData')->with('tier_price')->willReturn(1);
182+
183+
$this->updateHandler->execute($product);
184+
}
185+
}

0 commit comments

Comments
 (0)