Skip to content

Commit 94784e6

Browse files
author
Yu Tang
committed
MAGETWO-36325: Catalog Prices on Frontend do not include the cost of required Custom Option
1 parent 472acd6 commit 94784e6

File tree

9 files changed

+422
-47
lines changed

9 files changed

+422
-47
lines changed

app/code/Magento/Bundle/Pricing/Price/BundleRegularPrice.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use Magento\Bundle\Pricing\Adjustment\BundleCalculatorInterface;
1010
use Magento\Catalog\Model\Product;
1111
use Magento\Framework\Pricing\Amount\AmountInterface;
12+
use Magento\Catalog\Pricing\Price\CustomOptionPrice;
13+
use Magento\Bundle\Model\Product\Price;
1214

1315
/**
1416
* Bundle product regular price model
@@ -48,7 +50,13 @@ public function __construct(
4850
public function getAmount()
4951
{
5052
if (null === $this->amount) {
51-
$this->amount = $this->calculator->getMinRegularAmount($this->getValue(), $this->product);
53+
$price = $this->getValue();
54+
if ($this->product->getPriceType() == Price::PRICE_TYPE_FIXED) {
55+
/** @var \Magento\Catalog\Pricing\Price\CustomOptionPrice $customOptionPrice */
56+
$customOptionPrice = $this->priceInfo->getPrice(CustomOptionPrice::PRICE_CODE);
57+
$price += $customOptionPrice->getCustomOptionRange(true);
58+
}
59+
$this->amount = $this->calculator->getMinRegularAmount($price, $this->product);
5260
}
5361
return $this->amount;
5462
}
@@ -61,7 +69,13 @@ public function getAmount()
6169
public function getMaximalPrice()
6270
{
6371
if (null === $this->maximalPrice) {
64-
$this->maximalPrice = $this->calculator->getMaxRegularAmount($this->getValue(), $this->product);
72+
$price = $this->getValue();
73+
if ($this->product->getPriceType() == Price::PRICE_TYPE_FIXED) {
74+
/** @var \Magento\Catalog\Pricing\Price\CustomOptionPrice $customOptionPrice */
75+
$customOptionPrice = $this->priceInfo->getPrice(CustomOptionPrice::PRICE_CODE);
76+
$price += $customOptionPrice->getCustomOptionRange(false);
77+
}
78+
$this->maximalPrice = $this->calculator->getMaxRegularAmount($price, $this->product);
6579
}
6680
return $this->maximalPrice;
6781
}

app/code/Magento/Bundle/Pricing/Price/FinalPrice.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
use Magento\Catalog\Model\Product;
1010
use Magento\Framework\Pricing\Adjustment\CalculatorInterface;
11+
use Magento\Catalog\Pricing\Price\CustomOptionPrice;
12+
use Magento\Bundle\Model\Product\Price;
1113

1214
/**
1315
* Final price model
@@ -68,7 +70,13 @@ public function getValue()
6870
public function getMaximalPrice()
6971
{
7072
if (!$this->maximalPrice) {
71-
$this->maximalPrice = $this->calculator->getMaxAmount($this->getBasePrice()->getValue(), $this->product);
73+
$price = $this->getBasePrice()->getValue();
74+
if ($this->product->getPriceType() == Price::PRICE_TYPE_FIXED) {
75+
/** @var \Magento\Catalog\Pricing\Price\CustomOptionPrice $customOptionPrice */
76+
$customOptionPrice = $this->priceInfo->getPrice(CustomOptionPrice::PRICE_CODE);
77+
$price += $customOptionPrice->getCustomOptionRange(false);
78+
}
79+
$this->maximalPrice = $this->calculator->getMaxAmount($price, $this->product);
7280
}
7381
return $this->maximalPrice;
7482
}
@@ -91,7 +99,13 @@ public function getMinimalPrice()
9199
public function getAmount()
92100
{
93101
if (!$this->minimalPrice) {
94-
$this->minimalPrice = $this->calculator->getAmount(parent::getValue(), $this->product);
102+
$price = parent::getValue();
103+
if ($this->product->getPriceType() == Price::PRICE_TYPE_FIXED) {
104+
/** @var \Magento\Catalog\Pricing\Price\CustomOptionPrice $customOptionPrice */
105+
$customOptionPrice = $this->priceInfo->getPrice(CustomOptionPrice::PRICE_CODE);
106+
$price += $customOptionPrice->getCustomOptionRange(true);
107+
}
108+
$this->minimalPrice = $this->calculator->getAmount($price, $this->product);
95109
}
96110
return $this->minimalPrice;
97111
}

app/code/Magento/Bundle/Test/Unit/Pricing/Price/BundleRegularPriceTest.php

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
namespace Magento\Bundle\Test\Unit\Pricing\Price;
88

99
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
10+
use Magento\Catalog\Pricing\Price\CustomOptionPrice;
11+
use Magento\Bundle\Model\Product\Price;
1012

1113
class BundleRegularPriceTest extends \PHPUnit_Framework_TestCase
1214
{
@@ -25,6 +27,9 @@ class BundleRegularPriceTest extends \PHPUnit_Framework_TestCase
2527
/** @var \Magento\Framework\Pricing\PriceInfo\Base |\PHPUnit_Framework_MockObject_MockObject */
2628
protected $priceInfoMock;
2729

30+
/** @var CustomOptionPrice|\PHPUnit_Framework_MockObject_MockObject */
31+
protected $customOptionPriceMock;
32+
2833
/**
2934
* @var int
3035
*/
@@ -40,11 +45,18 @@ class BundleRegularPriceTest extends \PHPUnit_Framework_TestCase
4045
*/
4146
protected function setUp()
4247
{
43-
$this->saleableInterfaceMock = $this->getMock('Magento\Catalog\Model\Product', [], [], '', false);
48+
$this->saleableInterfaceMock = $this->getMockBuilder('\Magento\Catalog\Model\Product')
49+
->disableOriginalConstructor()
50+
->setMethods(['getPriceInfo', 'getPriceType', 'getPrice'])
51+
->getMock();
4452
$this->bundleCalculatorMock = $this->getMock('Magento\Bundle\Pricing\Adjustment\BundleCalculatorInterface');
4553

4654
$this->priceInfoMock = $this->getMock('Magento\Framework\Pricing\PriceInfo\Base', [], [], '', false);
4755

56+
$this->customOptionPriceMock = $this->getMockBuilder('\Magento\Catalog\Pricing\Price\CustomOptionPrice')
57+
->disableOriginalConstructor()
58+
->getMock();
59+
4860
$this->saleableInterfaceMock->expects($this->once())
4961
->method('getPriceInfo')
5062
->will($this->returnValue($this->priceInfoMock));
@@ -110,6 +122,48 @@ public function testGetMaximalPrice()
110122
$this->assertEquals($expectedResult, $result, 'Incorrect amount the second time');
111123
}
112124

125+
public function testGetMaximalPriceForFixedPriceBundleWithOption()
126+
{
127+
$price = 5;
128+
$maxOptionPrice = 2;
129+
130+
$expectedPrice = $price + $maxOptionPrice;
131+
132+
$this->priceInfoMock->expects($this->atLeastOnce())
133+
->method('getPrice')
134+
->with(CustomOptionPrice::PRICE_CODE)
135+
->willReturn($this->customOptionPriceMock);
136+
137+
$this->customOptionPriceMock->expects($this->once())
138+
->method('getCustomOptionRange')
139+
->with(false)
140+
->willReturn($maxOptionPrice);
141+
142+
$this->saleableInterfaceMock->expects($this->once())
143+
->method('getPriceType')
144+
->willReturn(Price::PRICE_TYPE_FIXED);
145+
146+
$this->saleableInterfaceMock->expects($this->once())
147+
->method('getPrice')
148+
->will($this->returnValue($price));
149+
150+
$this->bundleCalculatorMock->expects($this->once())
151+
->method('getMaxRegularAmount')
152+
->with($expectedPrice, $this->saleableInterfaceMock)
153+
->will($this->returnValue($expectedPrice));
154+
155+
$this->priceCurrencyMock->expects($this->once())
156+
->method('convertAndRound')
157+
->will($this->returnArgument(0));
158+
159+
$result = $this->regularPrice->getMaximalPrice();
160+
$this->assertEquals($expectedPrice, $result, 'Incorrect amount');
161+
162+
//Calling a second time, should use cached value
163+
$result = $this->regularPrice->getMaximalPrice();
164+
$this->assertEquals($expectedPrice, $result, 'Incorrect amount the second time');
165+
}
166+
113167
public function testGetMinimalPrice()
114168
{
115169
$expectedResult = 5;
@@ -134,4 +188,45 @@ public function testGetMinimalPrice()
134188
$result = $this->regularPrice->getMinimalPrice();
135189
$this->assertEquals($expectedResult, $result, 'Incorrect amount the second time');
136190
}
191+
192+
public function testGetMinimalPriceForFixedPricedBundleWithOptions()
193+
{
194+
$price = 5;
195+
$minOptionPrice = 1;
196+
$expectedValue = $price + $minOptionPrice;
197+
198+
$this->saleableInterfaceMock->expects($this->once())
199+
->method('getPrice')
200+
->will($this->returnValue($price));
201+
202+
$this->saleableInterfaceMock->expects($this->once())
203+
->method('getPriceType')
204+
->willReturn(Price::PRICE_TYPE_FIXED);
205+
206+
$this->priceInfoMock->expects($this->atLeastOnce())
207+
->method('getPrice')
208+
->with(CustomOptionPrice::PRICE_CODE)
209+
->willReturn($this->customOptionPriceMock);
210+
211+
$this->customOptionPriceMock->expects($this->once())
212+
->method('getCustomOptionRange')
213+
->with(true)
214+
->willReturn($minOptionPrice);
215+
216+
$this->priceCurrencyMock->expects($this->once())
217+
->method('convertAndRound')
218+
->will($this->returnArgument(0));
219+
220+
$this->bundleCalculatorMock->expects($this->once())
221+
->method('getMinRegularAmount')
222+
->with($expectedValue, $this->saleableInterfaceMock)
223+
->will($this->returnValue($expectedValue));
224+
225+
$result = $this->regularPrice->getMinimalPrice();
226+
$this->assertEquals($expectedValue, $result, 'Incorrect amount');
227+
228+
//Calling a second time, should use cached value
229+
$result = $this->regularPrice->getMinimalPrice();
230+
$this->assertEquals($expectedValue, $result, 'Incorrect amount the second time');
231+
}
137232
}

app/code/Magento/Bundle/Test/Unit/Pricing/Price/FinalPriceTest.php

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
namespace Magento\Bundle\Test\Unit\Pricing\Price;
88
use Magento\Bundle\Pricing\Price\BundleOptionPrice;
9+
use Magento\Catalog\Pricing\Price\CustomOptionPrice;
10+
use Magento\Bundle\Model\Product\Price;
911

1012
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
1113

@@ -38,6 +40,9 @@ class FinalPriceTest extends \PHPUnit_Framework_TestCase
3840
/** @var BundleOptionPrice|\PHPUnit_Framework_MockObject_MockObject */
3941
protected $bundleOptionMock;
4042

43+
/** @var CustomOptionPrice|\PHPUnit_Framework_MockObject_MockObject */
44+
protected $customOptionPriceMock;
45+
4146
/**
4247
* @var \Magento\Framework\Pricing\PriceCurrencyInterface|\PHPUnit_Framework_MockObject_MockObject
4348
*/
@@ -48,7 +53,10 @@ class FinalPriceTest extends \PHPUnit_Framework_TestCase
4853
*/
4954
protected function prepareMock()
5055
{
51-
$this->saleableInterfaceMock = $this->getMock('Magento\Catalog\Model\Product', [], [], '', false);
56+
$this->saleableInterfaceMock = $this->getMockBuilder('\Magento\Catalog\Model\Product')
57+
->disableOriginalConstructor()
58+
->setMethods(['getPriceType', 'getPriceInfo'])
59+
->getMock();
5260
$this->bundleCalculatorMock = $this->getMock('Magento\Bundle\Pricing\Adjustment\BundleCalculatorInterface');
5361

5462
$this->basePriceMock = $this->getMock('Magento\Catalog\Pricing\Price\BasePrice', [], [], '', false);
@@ -60,13 +68,18 @@ protected function prepareMock()
6068
->disableOriginalConstructor()
6169
->getMock();
6270

71+
$this->customOptionPriceMock = $this->getMockBuilder('\Magento\Catalog\Pricing\Price\CustomOptionPrice')
72+
->disableOriginalConstructor()
73+
->getMock();
74+
6375
$this->priceInfoMock = $this->getMock('Magento\Framework\Pricing\PriceInfo\Base', [], [], '', false);
6476

6577
$this->priceInfoMock->expects($this->atLeastOnce())
6678
->method('getPrice')
6779
->will($this->returnValueMap([
6880
[\Magento\Catalog\Pricing\Price\BasePrice::PRICE_CODE, $this->basePriceMock],
6981
[BundleOptionPrice::PRICE_CODE, $this->bundleOptionMock],
82+
[CustomOptionPrice::PRICE_CODE, $this->customOptionPriceMock],
7083
]));
7184

7285
$this->saleableInterfaceMock->expects($this->once())
@@ -128,6 +141,54 @@ public function testGetMaximalPrice($baseAmount)
128141
$this->assertSame($result, $this->finalPrice->getMaximalPrice());
129142
}
130143

144+
public function testGetMaximalPriceFixedBundleWithOption()
145+
{
146+
$optionMaxPrice = 2;
147+
$this->baseAmount = 5;
148+
$result = 7;
149+
$this->prepareMock();
150+
151+
$this->saleableInterfaceMock->expects($this->once())
152+
->method('getPriceType')
153+
->willReturn(Price::PRICE_TYPE_FIXED);
154+
$this->customOptionPriceMock->expects($this->once())
155+
->method('getCustomOptionRange')
156+
->with(false)
157+
->willReturn($optionMaxPrice);
158+
159+
$this->bundleCalculatorMock->expects($this->once())
160+
->method('getMaxAmount')
161+
->with($this->equalTo($this->baseAmount + $optionMaxPrice), $this->equalTo($this->saleableInterfaceMock))
162+
->will($this->returnValue($result));
163+
$this->assertSame($result, $this->finalPrice->getMaximalPrice());
164+
//The second call should use cached value
165+
$this->assertSame($result, $this->finalPrice->getMaximalPrice());
166+
}
167+
168+
public function testGetMinimalPriceFixedBundleWithOption()
169+
{
170+
$optionMaxPrice = 2;
171+
$this->baseAmount = 5;
172+
$result = 7;
173+
$this->prepareMock();
174+
175+
$this->saleableInterfaceMock->expects($this->once())
176+
->method('getPriceType')
177+
->willReturn(Price::PRICE_TYPE_FIXED);
178+
$this->customOptionPriceMock->expects($this->once())
179+
->method('getCustomOptionRange')
180+
->with(true)
181+
->willReturn($optionMaxPrice);
182+
183+
$this->bundleCalculatorMock->expects($this->once())
184+
->method('getAmount')
185+
->with($this->equalTo($this->baseAmount + $optionMaxPrice), $this->equalTo($this->saleableInterfaceMock))
186+
->will($this->returnValue($result));
187+
$this->assertSame($result, $this->finalPrice->getMinimalPrice());
188+
//The second call should use cached value
189+
$this->assertSame($result, $this->finalPrice->getMinimalPrice());
190+
}
191+
131192
/**
132193
* @dataProvider getValueDataProvider
133194
*/

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
126126
*/
127127
protected $_options = [];
128128

129+
/**
130+
* @var bool
131+
*/
132+
protected $optionsInitialized = false;
133+
129134
/**
130135
* @var array
131136
*/
@@ -1898,6 +1903,13 @@ public function getOptionById($optionId)
18981903
*/
18991904
public function getOptions()
19001905
{
1906+
if (empty($this->_options) && $this->getHasOptions() && !$this->optionsInitialized) {
1907+
foreach ($this->getProductOptionsCollection() as $option) {
1908+
$option->setProduct($this);
1909+
$this->addOption($option);
1910+
}
1911+
$this->optionsInitialized = true;
1912+
}
19011913
return $this->_options;
19021914
}
19031915

@@ -1911,6 +1923,7 @@ public function setOptions(array $options = null)
19111923
if (is_array($options) && empty($options)) {
19121924
$this->setData('is_delete_options', true);
19131925
}
1926+
$this->optionsInitialized = true;
19141927
return $this;
19151928
}
19161929

0 commit comments

Comments
 (0)