Skip to content

Commit 2c1ccdd

Browse files
committed
Merge remote-tracking branch 'origin/MAGETWO-90381' into 2.2-develop-pr59
2 parents 65c9a58 + e38a00d commit 2c1ccdd

File tree

14 files changed

+364
-13
lines changed

14 files changed

+364
-13
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Bundle\Model\Plugin;
9+
10+
use Magento\Quote\Model\Quote\Item as OrigQuoteItem;
11+
use Magento\Quote\Model\Quote\Item\AbstractItem;
12+
use Magento\Framework\Serialize\SerializerInterface;
13+
14+
/**
15+
* Update prices stored in quote item options after calculating quote item's totals.
16+
*/
17+
class UpdatePriceInQuoteItemOptions
18+
{
19+
/**
20+
* @var SerializerInterface
21+
*/
22+
private $serializer;
23+
24+
/**
25+
* @param SerializerInterface $serializer
26+
*/
27+
public function __construct(SerializerInterface $serializer)
28+
{
29+
$this->serializer = $serializer;
30+
}
31+
32+
/**
33+
* Update price on quote item options level
34+
*
35+
* @param OrigQuoteItem $subject
36+
* @param AbstractItem $result
37+
* @return AbstractItem
38+
*
39+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
40+
*/
41+
public function afterCalcRowTotal(OrigQuoteItem $subject, AbstractItem $result): AbstractItem
42+
{
43+
$bundleAttributes = $result->getProduct()->getCustomOption('bundle_selection_attributes');
44+
if ($bundleAttributes !== null) {
45+
$actualAmount = $result->getPrice() * $result->getQty();
46+
$parsedValue = $this->serializer->unserialize($bundleAttributes->getValue());
47+
if (is_array($parsedValue) && array_key_exists('price', $parsedValue)) {
48+
$parsedValue['price'] = $actualAmount;
49+
}
50+
$bundleAttributes->setValue($this->serializer->serialize($parsedValue));
51+
}
52+
53+
return $result;
54+
}
55+
}

app/code/Magento/Bundle/Model/Product/Type.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,7 @@ protected function _prepareProduct(\Magento\Framework\DataObject $buyRequest, $p
741741
$price = $product->getPriceModel()
742742
->getSelectionFinalTotalPrice($product, $selection, 0, $qty);
743743
$attributes = [
744-
'price' => $this->priceCurrency->convert($price),
744+
'price' => $price,
745745
'qty' => $qty,
746746
'option_label' => $selection->getOption()
747747
->getTitle(),

app/code/Magento/Bundle/Test/Mftf/ActionGroup/BundleProductsOnAdminActionGroup.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,8 @@
4747
<click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/>
4848
<click selector="{{AdminProductGridActionSection.addBundleProduct}}" stepKey="goToNewBundleProductPage"/>
4949
</actionGroup>
50+
51+
<actionGroup name="CreateBundleProductForTwoSimpleProductsWithRadioTypeOptions" extends="CreateBundleProductForTwoSimpleProducts">
52+
<selectOption selector="{{AdminProductFormBundleSection.bundleOptionXInputType('0')}}" userInput="Radio Buttons" after="fillOptionTitle" stepKey="selectInputType"/>
53+
</actionGroup>
5054
</actionGroups>

app/code/Magento/Bundle/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,20 @@
2323
<waitForText userInput="{{quantity}}" selector="{{StorefrontMinicartSection.productCount}}" time="30" stepKey="assertProductCount"/>
2424
<see userInput="You added {{product.name}} to your shopping cart." selector="{{StorefrontMessagesSection.success}}" stepKey="seeSuccessMessage"/>
2525
</actionGroup>
26+
<!-- Add Bundle Product to Cart with specified currency -->
27+
<actionGroup name="StoreFrontAddProductToCartFromBundleWithCurrencyActionGroup">
28+
<arguments>
29+
<argument name="product"/>
30+
<argument name="currency" type="string" defaultValue="US Dollar"/>
31+
</arguments>
32+
<click selector="{{StorefrontHeaderCurrencySwitcherSection.currencyTrigger}}" stepKey="openCurrencyTrigger"/>
33+
<click selector="{{StorefrontHeaderCurrencySwitcherSection.currency(currency)}}" stepKey="chooseCurrency"/>
34+
<waitForPageLoad stepKey="waitForCurrencyChange"/>
35+
<click selector="{{StorefrontBundledSection.addToCart}}" stepKey="clickCustomize"/>
36+
<waitForPageLoad stepKey="waitForBundleOpen"/>
37+
<checkOption selector="{{StorefrontBundledSection.bundleOptionByName(product.name)}}" stepKey="chooseProduct"/>
38+
<click selector="{{StorefrontBundledSection.addToCartConfigured}}" stepKey="addToCartProduct"/>
39+
<waitForAjaxLoad stepKey="waitForLoad"/>
40+
<scrollToTopOfPage stepKey="scrollToTop"/>
41+
</actionGroup>
2642
</actionGroups>

app/code/Magento/Bundle/Test/Mftf/Data/BundleProductData.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
<data key="name" unique="suffix">BundleProduct</data>
1818
<data key="sku" unique="suffix">BundleProduct</data>
1919
<data key="status">1</data>
20+
<data key="set">4</data>
21+
<data key="type">bundle</data>
2022
<data key="urlKey" unique="suffix">bundleproduct</data>
2123
<data key="visibility">4</data>
2224
<data key="option_title" unique="suffix">TestOption</data>

app/code/Magento/Bundle/Test/Mftf/Section/StorefrontBundledSection.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/SectionObject.xsd">
1111
<section name="StorefrontBundledSection">
1212
<element name="bundleOption" type="input" selector=".option:nth-of-type({{numOption}}) .choice:nth-of-type({{numOptionSelect}}) input" parameterized="true"/>
13+
<element name="bundleOptionByName" type="input" selector="//div[@class='field choice']//span[@class='product-name'][contains(text(),'{{name}}')]/../../../input" parameterized="true"/>
1314
<element name="addToCart" type="button" selector="#bundle-slide" timeout="30"/>
1415
<element name="addToCartConfigured" type="button" selector="#product-addtocart-button" timeout="30"/>
1516
<element name="updateCart" type="button" selector="#product-updatecart-button" timeout="30"/>
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
9+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
10+
<test name="CurrencyChangingBundleProductInCartTest">
11+
<annotations>
12+
<features value="Bundle"/>
13+
<stories value="MAGETWO-90381: Bundle product price doubled when switching currency"/>
14+
<title value="Work of currency changing with a bundle product added to the cart"/>
15+
<description value="User should be able change the currency and add one more product in cart and get right price in previous currency"/>
16+
<severity value="MAJOR"/>
17+
<testCaseId value="MAGETWO-96305"/>
18+
<group value="Bundle"/>
19+
</annotations>
20+
<before>
21+
<actionGroup ref="LoginAsAdmin" stepKey="login" />
22+
<createData entity="CurrencySettingWithEuroAndUSD" stepKey="configureCurrencyOptions"/>
23+
<createData entity="_defaultCategory" stepKey="createPreReqCategory"/>
24+
<createData entity="SimpleProduct" stepKey="createPreReqSimpleProduct1">
25+
<requiredEntity createDataKey="createPreReqCategory"/>
26+
</createData>
27+
<createData entity="SimpleProduct2" stepKey="createPreReqSimpleProduct2">
28+
<requiredEntity createDataKey="createPreReqCategory"/>
29+
</createData>
30+
</before>
31+
<after>
32+
<deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/>
33+
<deleteData createDataKey="createPreReqSimpleProduct1" stepKey="deletePreReqSimpleProduct1"/>
34+
<deleteData createDataKey="createPreReqSimpleProduct2" stepKey="deletePreReqSimpleProduct2"/>
35+
<createData entity="DefaultCurrencySetting" stepKey="restoreCurrencyOptions"/>
36+
<!-- Delete the bundled product -->
37+
<actionGroup ref="deleteProductUsingProductGrid" stepKey="deleteProductOnProductsGridPageByName">
38+
<argument name="product" value="BundleProduct"/>
39+
</actionGroup>
40+
<!--Clear Configs-->
41+
<actionGroup ref="logout" stepKey="logout"/>
42+
</after>
43+
<!-- Navigate to the Products>Inventory>Catalog -->
44+
<!-- Click on "+" dropdown and select Bundle Product type -->
45+
<actionGroup ref="OpenNewBundleProductPage" stepKey="openNewBundleProductPage"/>
46+
<!-- Add Option, a "Radio Buttons" type option -->
47+
<actionGroup ref="CreateBundleProductForTwoSimpleProductsWithRadioTypeOptions" stepKey="addBundleOptionWithTwoProducts2">
48+
<argument name="bundleProduct" value="BundleProduct"/>
49+
<argument name="simpleProductFirst" value="$$createPreReqSimpleProduct1$$"/>
50+
<argument name="simpleProductSecond" value="$$createPreReqSimpleProduct2$$"/>
51+
</actionGroup>
52+
<!-- Save product -->
53+
<actionGroup ref="SaveProductOnProductPageOnAdmin" stepKey="saveProductOnProductPageOnAdmin"/>
54+
<!-- Go to storefront BundleProduct -->
55+
<amOnPage url="{{StorefrontProductPage.url(BundleProduct.name)}}" stepKey="goToStorefrontProductPage"/>
56+
<waitForPageLoad stepKey="waitForStorefrontProductPage"/>
57+
<actionGroup ref="StoreFrontAddProductToCartFromBundleWithCurrencyActionGroup" stepKey="addProduct1ToCartAndChangeCurrencyToEuro">
58+
<argument name="product" value="$$createPreReqSimpleProduct1$$"/>
59+
<argument name="currency" value="EUR - Euro"/>
60+
</actionGroup>
61+
<actionGroup ref="StoreFrontAddProductToCartFromBundleWithCurrencyActionGroup" stepKey="addProduct2ToCartAndChangeCurrencyToUSD">
62+
<argument name="product" value="$$createPreReqSimpleProduct1$$"/>
63+
<argument name="currency" value="USD - US Dollar"/>
64+
</actionGroup>
65+
<click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniCart"/>
66+
<waitForPageLoad stepKey="waitForMiniCart"/>
67+
<see selector="{{StorefrontMinicartSection.miniCartSubtotalField}}" userInput="$4,000.00" stepKey="seeCartSubtotal"/>
68+
</test>
69+
</tests>
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Bundle\Test\Unit\Model\Plugin;
7+
8+
use Magento\Bundle\Model\Plugin\UpdatePriceInQuoteItemOptions;
9+
use Magento\Catalog\Model\Product;
10+
use Magento\Framework\Serialize\SerializerInterface;
11+
use Magento\Quote\Model\Quote\Item\AbstractItem;
12+
use Magento\Quote\Model\Quote\Item as QuoteItem;
13+
use Magento\Quote\Model\Quote\Item\Option;
14+
15+
/**
16+
* Test for Magento\Bundle\Model\Plugin\UpdatePriceInQuoteItemOptions class.
17+
*/
18+
class UpdatePriceInQuoteItemOptionsTest extends \PHPUnit\Framework\TestCase
19+
{
20+
/**
21+
* @var SerializerInterface|\PHPUnit_Framework_MockObject_MockObject
22+
*/
23+
private $serializerMock;
24+
25+
/**
26+
* @var QuoteItem|\PHPUnit_Framework_MockObject_MockObject
27+
*/
28+
private $subjectMock;
29+
30+
/**
31+
* @var AbstractItem|\PHPUnit_Framework_MockObject_MockObject
32+
*/
33+
private $resultMock;
34+
35+
/**
36+
* @var Product|\PHPUnit_Framework_MockObject_MockObject
37+
*/
38+
private $productMock;
39+
40+
/**
41+
* @var Option|\PHPUnit_Framework_MockObject_MockObject
42+
*/
43+
private $quoteItemOptionMock;
44+
45+
/**
46+
* @var UpdatePriceInQuoteItemOptions
47+
*/
48+
private $model;
49+
50+
/**
51+
* @inheritdoc
52+
*/
53+
protected function setUp()
54+
{
55+
$this->serializerMock = $this->createMock(SerializerInterface::class);
56+
$this->subjectMock = $this->createMock(QuoteItem::class);
57+
$this->resultMock = $this->createMock(AbstractItem::class);
58+
$this->productMock = $this->createMock(Product::class);
59+
$this->quoteItemOptionMock = $this->createMock(Option::class);
60+
61+
$this->model = new UpdatePriceInQuoteItemOptions($this->serializerMock);
62+
}
63+
64+
/**
65+
* @return void
66+
*/
67+
public function testAfterCalcRowTotalWithBundleOption()
68+
{
69+
$bundleAttributeValue = '{"price":100,"qty":1,"option_label":"option1","option_id":"1"}';
70+
$parsedValue = [
71+
'price' => 100,
72+
'qty' => 1,
73+
'option_label' => 'option1',
74+
'option_id' => "1",
75+
];
76+
77+
$this->resultMock->expects($this->once())
78+
->method('getProduct')
79+
->willReturn($this->productMock);
80+
$this->productMock->expects($this->once())
81+
->method('getCustomOption')
82+
->with('bundle_selection_attributes')
83+
->willReturn($this->quoteItemOptionMock);
84+
$this->resultMock->expects($this->once())
85+
->method('getPrice')
86+
->willReturn(100);
87+
$this->resultMock->expects($this->once())
88+
->method('getQty')
89+
->willReturn(1);
90+
$this->quoteItemOptionMock->expects($this->once())
91+
->method('getValue')
92+
->willReturn($bundleAttributeValue);
93+
$this->serializerMock->expects($this->once())
94+
->method('unserialize')
95+
->with($bundleAttributeValue)
96+
->willReturn($parsedValue);
97+
$this->serializerMock->expects($this->once())
98+
->method('serialize')
99+
->with($parsedValue)
100+
->willReturn($bundleAttributeValue);
101+
102+
$this->model->afterCalcRowTotal($this->subjectMock, $this->resultMock);
103+
}
104+
105+
/**
106+
* @return void
107+
*/
108+
public function testAfterCalcRowTotalWithoutBundleOption()
109+
{
110+
$this->resultMock->expects($this->once())
111+
->method('getProduct')
112+
->willReturn($this->productMock);
113+
$this->productMock->expects($this->once())
114+
->method('getCustomOption')
115+
->with('bundle_selection_attributes')
116+
->willReturn(null);
117+
$this->resultMock->expects($this->never())
118+
->method('getPrice');
119+
$this->resultMock->expects($this->never())
120+
->method('getQty');
121+
$this->quoteItemOptionMock->expects($this->never())
122+
->method('getValue');
123+
$this->serializerMock->expects($this->never())
124+
->method('unserialize');
125+
$this->serializerMock->expects($this->never())
126+
->method('serialize');
127+
128+
$this->model->afterCalcRowTotal($this->subjectMock, $this->resultMock);
129+
}
130+
}

app/code/Magento/Bundle/Test/Unit/Model/Product/TypeTest.php

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -513,10 +513,6 @@ function ($key) use ($optionCollection, $selectionCollection) {
513513
->method('getSelectionId')
514514
->willReturn(314);
515515

516-
$this->priceCurrency->expects($this->once())
517-
->method('convert')
518-
->willReturn(3.14);
519-
520516
$result = $this->model->prepareForCartAdvanced($buyRequest, $product);
521517
$this->assertEquals([$product, $productType], $result);
522518
}
@@ -737,10 +733,6 @@ function ($key) use ($optionCollection, $selectionCollection) {
737733
->method('prepareForCart')
738734
->willReturn([]);
739735

740-
$this->priceCurrency->expects($this->once())
741-
->method('convert')
742-
->willReturn(3.14);
743-
744736
$result = $this->model->prepareForCartAdvanced($buyRequest, $product);
745737
$this->assertEquals('We can\'t add this item to your shopping cart right now.', $result);
746738
}
@@ -961,10 +953,6 @@ function ($key) use ($optionCollection, $selectionCollection) {
961953
->method('prepareForCart')
962954
->willReturn('string');
963955

964-
$this->priceCurrency->expects($this->once())
965-
->method('convert')
966-
->willReturn(3.14);
967-
968956
$result = $this->model->prepareForCartAdvanced($buyRequest, $product);
969957
$this->assertEquals('string', $result);
970958
}

app/code/Magento/Bundle/etc/di.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@
123123
</argument>
124124
</arguments>
125125
</type>
126+
<type name="Magento\Quote\Model\Quote\Item">
127+
<plugin name="update_price_for_bundle_in_quote_item_option" type="Magento\Bundle\Model\Plugin\UpdatePriceInQuoteItemOptions"/>
128+
</type>
126129
<type name="Magento\Quote\Model\Quote\Item\ToOrderItem">
127130
<plugin name="append_bundle_data_to_order" type="Magento\Bundle\Model\Plugin\QuoteItem"/>
128131
</type>

0 commit comments

Comments
 (0)