Skip to content

Commit 783f8b0

Browse files
🔃 [Magento Community Engineering] Community Contributions - 2.4.1-develop expedited
Accepted Community Pull Requests: - #27455: #23440 fix Refund for bundle product without receiving product back e… (by @KiuNguyen) - #27572: Fix widget recent view product not fit in mobile view (by @mrtuvn) - #27359: Issue 27358 (by @RHapani) Fixed GitHub Issues: - #23440: Refund for bundle product without receiving product back (reported by @simonmaass) has been fixed in #27455 by @KiuNguyen in 2.4.1-develop branch Related commits: 1. d0ab2a3 2. d03d133 3. 4807ebb 4. b88d88e 5. 10d5f73 6. b13b0ec 7. 4b28ae3 8. 9de9cf0 9. a7ea256 10. df1b766 11. 84ff076 12. a4d0b79 - #27058: Recently viewed products in Luma theme does not fit on screen (reported by @Zyles) has been fixed in #27572 by @mrtuvn in 2.4.1-develop branch Related commits: 1. d7cf188 2. 9c81433 3. 4b64f33 4. 5f0c02b 5. 581ab1f 6. a4935e3 7. ce431d3 - #27358: Advance search page allow minus price filter (reported by @RHapani) has been fixed in #27359 by @RHapani in 2.4.1-develop branch Related commits: 1. 0e964e4 2. 3c64566 3. 633b313 4. 445156b 5. a1f37fb 6. 09b7ebd
2 parents 3f1099e + 16dd439 commit 783f8b0

File tree

11 files changed

+376
-23
lines changed

11 files changed

+376
-23
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
9+
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
11+
<actionGroup name="StorefrontAssertUnableSearchNegativeForPriceFieldActionGroup" extends="StorefrontFillFormAdvancedSearchActionGroup">
12+
<remove keyForRemoval="waitForPageLoad" />
13+
<grabTextFrom selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceFromError}}" stepKey="grabPriceFromError"/>
14+
<grabTextFrom selector="{{StorefrontCatalogSearchAdvancedFormSection.PriceToError}}" stepKey="grabPriceToError"/>
15+
<assertEquals stepKey="assertErrorMessagePriceFrom">
16+
<actualResult type="string">{{UnableNegativePrice.Error_message}}</actualResult>
17+
<expectedResult type="string">$grabPriceFromError</expectedResult>
18+
</assertEquals>
19+
<assertEquals stepKey="assertErrorMessagePriceTo">
20+
<actualResult type="string">{{UnableNegativePrice.Error_message}}</actualResult>
21+
<expectedResult type="string">$grabPriceToError</expectedResult>
22+
</assertEquals>
23+
</actionGroup>
24+
</actionGroups>

app/code/Magento/CatalogSearch/Test/Mftf/Data/MinMaxQueryLengthHintsData.xml renamed to app/code/Magento/CatalogSearch/Test/Mftf/Data/MessageAndHintData.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,7 @@
1111
<entity name="MinMaxQueryLength" type="constant">
1212
<data key="Hint">This value must be compatible with the corresponding setting in the configured search engine</data>
1313
</entity>
14+
<entity name="UnableNegativePrice" type="constant">
15+
<data key="Error_message">Please enter a number 0 or greater in this field.</data>
16+
</entity>
1417
</entities>

app/code/Magento/CatalogSearch/Test/Mftf/Section/StorefrontCatalogSearchAdvancedFormSection.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
<element name="ShortDescription" type="input" selector="#short_description"/>
1717
<element name="PriceFrom" type="input" selector="#price"/>
1818
<element name="PriceTo" type="input" selector="#price_to"/>
19+
<element name="PriceFromError" type="text" selector="#price-error"/>
20+
<element name="PriceToError" type="text" selector="#price_to-error"/>
1921
<element name="AttributeByCode" type="input" selector="#{{var1}}" parameterized="true"/>
2022
<element name="SubmitButton" type="button" selector="//*[@id='form-validate']//button[@type='submit']"/>
2123
</section>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
9+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
11+
<test name="StorefrontCheckUnableAdvancedSearchWithNegativePriceTest">
12+
<annotations>
13+
<stories value="Use Advanced Search"/>
14+
<title value="Unable negative price use to advanced search"/>
15+
<description value="Check unable negative price use to advanced search by price from and price to"/>
16+
</annotations>
17+
<actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefront"/>
18+
<actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="openAdvancedSearch"/>
19+
<actionGroup ref="StorefrontAssertUnableSearchNegativeForPriceFieldActionGroup" stepKey="assertUnableSearch">
20+
<argument name="price_to" value="-50"/>
21+
<argument name="price_from" value="-10"/>
22+
</actionGroup>
23+
</test>
24+
</tests>

app/code/Magento/CatalogSearch/view/frontend/templates/advanced/form.phtml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis
8+
// @codingStandardsIgnoreFile
89
?>
910
<?php
1011
/**
@@ -68,7 +69,7 @@
6869
class="input-text"
6970
type="text"
7071
maxlength="<?= $block->escapeHtmlAttr($maxQueryLength) ?>"
71-
data-validate="{number:true, 'less-than-equals-to':'#<?= $block->escapeHtmlAttr($_code) ?>_to'}" />
72+
data-validate="{number:true, 'validate-not-negative-number':true, 'less-than-equals-to':'#<?= $block->escapeHtmlAttr($_code) ?>_to'}" />
7273
</div>
7374
</div>
7475
<div class="field with-addon no-label">
@@ -81,7 +82,7 @@
8182
class="input-text"
8283
type="text"
8384
maxlength="<?= $block->escapeHtmlAttr($maxQueryLength) ?>"
84-
data-validate="{number:true, 'greater-than-equals-to':'#<?= $block->escapeHtmlAttr($_code) ?>'}" />
85+
data-validate="{number:true, 'validate-not-negative-number':true, 'greater-than-equals-to':'#<?= $block->escapeHtmlAttr($_code) ?>'}" />
8586
<label class="addafter"
8687
for="<?= $block->escapeHtmlAttr($_code) ?>_to">
8788
<?= $block->escapeHtml($block->getCurrency($_attribute)) ?>

app/code/Magento/Sales/Model/Order/CreditmemoFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ protected function canRefundItem($item, $qtys = [], $invoiceQtysRefundLimits = [
158158
if ($item->isDummy()) {
159159
if ($item->getHasChildren()) {
160160
foreach ($item->getChildrenItems() as $child) {
161-
if (empty($qtys)) {
161+
if (empty($qtys) || (count(array_unique($qtys)) === 1 && (int)end($qtys) === 0)) {
162162
if ($this->canRefundNoDummyItem($child, $invoiceQtysRefundLimits)) {
163163
return true;
164164
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
9+
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
11+
<actionGroup name="AdminOpenAndFillCreditMemoRefundBundleWithQtyActionGroup">
12+
<annotations>
13+
<description>Clicks on the 'Credit Memos' section on the Admin Orders edit page. Fills in the provided Refund details (child item qty, Shipping Refund, Adjustment Refund, Adjustment Fee and Row number).</description>
14+
</annotations>
15+
<arguments>
16+
<argument name="itemQtyToRefund" type="string" defaultValue="0"/>
17+
<argument name="shippingRefund" type="string" defaultValue="0"/>
18+
<argument name="adjustmentRefund" type="string" defaultValue="0"/>
19+
<argument name="adjustmentFee" type="string" defaultValue="0"/>
20+
<argument name="rowNumberItemOne" type="string" defaultValue="3"/>
21+
<argument name="rowNumberItemTwo" type="string" defaultValue="5"/>
22+
<argument name="rowNumberItemThree" type="string" defaultValue="6"/>
23+
</arguments>
24+
25+
<!-- Click 'Credit Memo' button -->
26+
<click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="clickCreateCreditMemo"/>
27+
<seeInCurrentUrl url="{{AdminCreditMemoNewPage.url}}" stepKey="seeNewCreditMemoPage"/>
28+
<see selector="{{AdminHeaderSection.pageTitle}}" userInput="New Memo" stepKey="seeNewMemoInPageTitle"/>
29+
30+
<!-- Fill data from dataset: refund -->
31+
<scrollTo selector="{{AdminCreditMemoItemsSection.header}}" stepKey="scrollToItemsToRefund"/>
32+
<fillField selector="{{AdminCreditMemoItemsSection.childItemQtyToRefund(rowNumberItemOne)}}" userInput="{{itemQtyToRefund}}" stepKey="fillQtyToRefundItemOne"/>
33+
<fillField selector="{{AdminCreditMemoItemsSection.childItemQtyToRefund(rowNumberItemTwo)}}" userInput="{{itemQtyToRefund}}" stepKey="fillQtyToRefundItemTwo"/>
34+
<fillField selector="{{AdminCreditMemoItemsSection.childItemQtyToRefund(rowNumberItemThree)}}" userInput="{{itemQtyToRefund}}" stepKey="fillQtyToRefundItemThree"/>
35+
<waitForLoadingMaskToDisappear stepKey="waitForActivateButton"/>
36+
<conditionalClick selector="{{AdminCreditMemoItemsSection.updateQty}}" dependentSelector="{{AdminCreditMemoItemsSection.disabledUpdateQty}}" visible="false" stepKey="clickUpdateButton"/>
37+
<waitForLoadingMaskToDisappear stepKey="waitForUpdate"/>
38+
<fillField userInput="{{shippingRefund}}" selector="{{AdminCreditMemoTotalSection.refundShipping}}" stepKey="fillShipping"/>
39+
<fillField userInput="{{adjustmentRefund}}" selector="{{AdminCreditMemoTotalSection.adjustmentRefund}}" stepKey="fillAdjustmentRefund"/>
40+
<fillField userInput="{{adjustmentFee}}" selector="{{AdminCreditMemoTotalSection.adjustmentFee}}" stepKey="fillAdjustmentFee"/>
41+
<waitForElementVisible selector="{{AdminCreditMemoTotalSection.updateTotals}}" stepKey="waitForUpdateTotalsButton"/>
42+
<click selector="{{AdminCreditMemoTotalSection.updateTotals}}" stepKey="clickUpdateTotals"/>
43+
<checkOption selector="{{AdminCreditMemoTotalSection.emailCopy}}" stepKey="checkSendEmailCopy"/>
44+
</actionGroup>
45+
</actionGroups>

app/code/Magento/Sales/Test/Mftf/Section/AdminCreditMemoItemsSection.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@
2424
<element name="disabledUpdateQty" type="button" selector=".order-creditmemo-tables tfoot button[data-ui-id='order-items-update-button'].disabled" timeout="30"/>
2525
<element name="nameColumn" type="text" selector=".order-creditmemo-tables .product-title"/>
2626
<element name="skuColumn" type="text" selector=".order-creditmemo-tables .product-sku-block"/>
27+
<element name="childItemQtyToRefund" type="input" selector=".order-creditmemo-tables tr:nth-child({{row}}) td .qty-input" parameterized="true"/>
2728
</section>
2829
</sections>
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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+
9+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
11+
<test name="AdminCreateCreditmemoWithBundleProductTest">
12+
<annotations>
13+
<title value="Create Creditmemo in Admin with bundle product"/>
14+
<stories value="Github issue: #23440 fix Refund for bundle product without receiving product back"/>
15+
<description value="Create Creditmemo for bundle product with without receiving product back(all child item qty = 0)"/>
16+
<features value="Sales"/>
17+
<severity value="AVERAGE"/>
18+
<group value="Sales"/>
19+
</annotations>
20+
21+
<before>
22+
<createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/>
23+
<createData entity="Simple_US_Customer_CA" stepKey="simpleCustomer"/>
24+
<createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/>
25+
<createData entity="ApiProductWithDescription" stepKey="simple2" before="product"/>
26+
<createData entity="ApiBundleProduct" stepKey="product"/>
27+
<createData entity="CheckboxOption" stepKey="checkboxBundleOption">
28+
<requiredEntity createDataKey="product"/>
29+
</createData>
30+
<createData entity="ApiBundleLink" stepKey="createBundleLink1">
31+
<requiredEntity createDataKey="product"/>
32+
<requiredEntity createDataKey="checkboxBundleOption"/>
33+
<requiredEntity createDataKey="simple1"/>
34+
<field key="qty">2</field>
35+
<field key="is_default">1</field>
36+
</createData>
37+
<createData entity="ApiBundleLink" stepKey="createBundleLink2">
38+
<requiredEntity createDataKey="product"/>
39+
<requiredEntity createDataKey="checkboxBundleOption"/>
40+
<requiredEntity createDataKey="simple2"/>
41+
<field key="qty">2</field>
42+
<field key="is_default">1</field>
43+
</createData>
44+
<createData entity="DropDownBundleOption" stepKey="dropDownBundleOption">
45+
<requiredEntity createDataKey="product"/>
46+
</createData>
47+
<createData entity="ApiBundleLink" stepKey="createBundleLink3">
48+
<requiredEntity createDataKey="product"/>
49+
<requiredEntity createDataKey="dropDownBundleOption"/>
50+
<requiredEntity createDataKey="simple1"/>
51+
<field key="qty">2</field>
52+
<field key="is_default">1</field>
53+
</createData>
54+
<createData entity="ApiBundleLink" stepKey="createBundleLink4">
55+
<requiredEntity createDataKey="product"/>
56+
<requiredEntity createDataKey="dropDownBundleOption"/>
57+
<requiredEntity createDataKey="simple2"/>
58+
<field key="qty">2</field>
59+
</createData>
60+
<actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
61+
</before>
62+
<after>
63+
<actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
64+
<deleteData createDataKey="product" stepKey="delete"/>
65+
<deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/>
66+
<deleteData createDataKey="simple1" stepKey="deleteSimple1" before="deleteSimple2"/>
67+
<deleteData createDataKey="simple2" stepKey="deleteSimple2" before="delete"/>
68+
</after>
69+
70+
<actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer">
71+
<argument name="customer" value="$$simpleCustomer$$"/>
72+
</actionGroup>
73+
<actionGroup ref="AddBundleProductToOrderAndCheckPriceInGridActionGroup" stepKey="addBundleProductToOrder">
74+
<argument name="product" value="$$product$$"/>
75+
<argument name="quantity" value="1"/>
76+
<argument name="price" value="$738.00"/>
77+
</actionGroup>
78+
<actionGroup ref="OrderSelectFlatRateShippingActionGroup" stepKey="orderSelectFlatRateShippingMethod"/>
79+
<actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/>
80+
<actionGroup ref="StartCreateInvoiceFromOrderPageActionGroup" stepKey="startInvoice"/>
81+
<actionGroup ref="SubmitInvoiceActionGroup" stepKey="submitInvoice"/>
82+
<grabFromCurrentUrl regex="~/order_id/(\d+)/~" stepKey="grabOrderId"/>
83+
<actionGroup ref="OpenOrderByIdActionGroup" stepKey="openOrder">
84+
<argument name="orderId" value="$grabOrderId"/>
85+
</actionGroup>
86+
<actionGroup ref="AdminOpenAndFillCreditMemoRefundBundleWithQtyActionGroup" stepKey="fillCreditMemoRefund">
87+
<argument name="itemQtyToRefund" value="0"/>
88+
<argument name="rowNumberItemOne" value="3"/>
89+
<argument name="rowNumberItemTwo" value="5"/>
90+
<argument name="rowNumberItemThree" value="6"/>
91+
<argument name="adjustmentRefund" value="10"/>
92+
</actionGroup>
93+
<actionGroup ref="SubmitCreditMemoActionGroup" stepKey="submitCreditMemo" />
94+
95+
<actionGroup ref="AdminOpenCreditMemoFromOrderPageActionGroup" stepKey="openCreditMemo" />
96+
<scrollTo selector="{{AdminCreditMemoViewTotalSection.subtotal}}" stepKey="scrollToTotal"/>
97+
<actionGroup ref="AssertAdminCreditMemoViewPageTotalsActionGroup" stepKey="assertCreditMemoViewPageTotals">
98+
<argument name="subtotal" value="$0.00"/>
99+
<argument name="adjustmentRefund" value="$10.00"/>
100+
<argument name="adjustmentFee" value="$0.00"/>
101+
<argument name="grandTotal" value="$10.00"/>
102+
</actionGroup>
103+
</test>
104+
</tests>
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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\Sales\Test\Unit\Model\Order;
9+
10+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
11+
use Magento\Sales\Model\Order\CreditmemoFactory;
12+
use Magento\Sales\Model\Order\Item;
13+
use PHPUnit\Framework\MockObject\MockObject;
14+
use PHPUnit\Framework\TestCase;
15+
use ReflectionMethod;
16+
17+
/**
18+
* Unit test for creditmemo factory class.
19+
*/
20+
class CreditmemoFactoryTest extends TestCase
21+
{
22+
/**
23+
* @var CreditmemoFactory
24+
*/
25+
protected $subject;
26+
27+
/**
28+
* @var ReflectionMethod
29+
*/
30+
protected $testMethod;
31+
32+
/**
33+
* @var Item|MockObject
34+
*/
35+
protected $orderItemMock;
36+
37+
/**
38+
* @var Item|MockObject
39+
*/
40+
protected $orderChildItemOneMock;
41+
42+
/**
43+
* @var Item|MockObject
44+
*/
45+
protected $orderChildItemTwoMock;
46+
47+
/**
48+
* @inheritDoc
49+
*/
50+
protected function setUp(): void
51+
{
52+
$this->orderItemMock = $this->createPartialMock(
53+
Item::class,
54+
['getChildrenItems', 'isDummy', 'getHasChildren', 'getId', 'getParentItemId']
55+
);
56+
$this->orderChildItemOneMock = $this->createPartialMock(
57+
Item::class,
58+
['getQtyToRefund', 'getId']
59+
);
60+
$this->orderChildItemTwoMock = $this->createPartialMock(
61+
Item::class,
62+
['getQtyToRefund', 'getId']
63+
);
64+
$this->testMethod = new ReflectionMethod(CreditmemoFactory::class, 'canRefundItem');
65+
66+
$objectManagerHelper = new ObjectManagerHelper($this);
67+
$this->subject = $objectManagerHelper->getObject(CreditmemoFactory::class, []);
68+
}
69+
70+
/**
71+
* Check if order item can be refunded
72+
* @return void
73+
*/
74+
public function testCanRefundItem(): void
75+
{
76+
$orderItemQtys = [
77+
2 => 0,
78+
3 => 0
79+
];
80+
$invoiceQtysRefundLimits = [];
81+
82+
$this->orderItemMock->expects($this->any())
83+
->method('getId')
84+
->willReturn(1);
85+
$this->orderItemMock->expects($this->any())
86+
->method('getParentItemId')
87+
->willReturn(false);
88+
$this->orderItemMock->expects($this->any())
89+
->method('isDummy')
90+
->willReturn(true);
91+
$this->orderItemMock->expects($this->any())
92+
->method('getHasChildren')
93+
->willReturn(true);
94+
95+
$this->orderChildItemOneMock->expects($this->any())
96+
->method('getQtyToRefund')
97+
->willReturn(1);
98+
$this->orderChildItemOneMock->expects($this->any())
99+
->method('getId')
100+
->willReturn(2);
101+
102+
$this->orderChildItemTwoMock->expects($this->any())
103+
->method('getQtyToRefund')
104+
->willReturn(1);
105+
$this->orderChildItemTwoMock->expects($this->any())
106+
->method('getId')
107+
->willReturn(3);
108+
$this->orderItemMock->expects($this->any())
109+
->method('getChildrenItems')
110+
->willReturn([$this->orderChildItemOneMock, $this->orderChildItemTwoMock]);
111+
112+
$this->testMethod->setAccessible(true);
113+
114+
$this->assertTrue(
115+
$this->testMethod->invoke(
116+
$this->subject,
117+
$this->orderItemMock,
118+
$orderItemQtys,
119+
$invoiceQtysRefundLimits
120+
)
121+
);
122+
}
123+
}

0 commit comments

Comments
 (0)