Skip to content

Commit 9a68d68

Browse files
authored
Merge pull request #6778 from magento-tsg-csl3/2.4-develop-pr56
[TSG-CSL3] For 2.4 (pr56)
2 parents c46d9bf + 4d01235 commit 9a68d68

File tree

6 files changed

+314
-15
lines changed

6 files changed

+314
-15
lines changed

app/code/Magento/Bundle/Model/ResourceModel/Indexer/Price.php

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,21 @@ class Price implements DimensionalIndexerInterface
9191
*/
9292
private $moduleManager;
9393

94+
/**
95+
* @var string
96+
*/
97+
private $tmpBundlePriceTable;
98+
99+
/**
100+
* @var string
101+
*/
102+
private $tmpBundleSelectionTable;
103+
104+
/**
105+
* @var string
106+
*/
107+
private $tmpBundleOptionTable;
108+
94109
/**
95110
* @param IndexTableStructureFactory $indexTableStructureFactory
96111
* @param TableMaintainer $tableMaintainer
@@ -184,7 +199,16 @@ public function executeByDimensions(array $dimensions, \Traversable $entityIds)
184199
*/
185200
private function getBundlePriceTable()
186201
{
187-
return $this->getTable('catalog_product_index_price_bundle_tmp');
202+
if ($this->tmpBundlePriceTable === null) {
203+
$this->tmpBundlePriceTable = $this->getTable('catalog_product_index_price_bundle_temp');
204+
$this->getConnection()->createTemporaryTableLike(
205+
$this->tmpBundlePriceTable,
206+
$this->getTable('catalog_product_index_price_bundle_tmp'),
207+
true
208+
);
209+
}
210+
211+
return $this->tmpBundlePriceTable;
188212
}
189213

190214
/**
@@ -194,7 +218,16 @@ private function getBundlePriceTable()
194218
*/
195219
private function getBundleSelectionTable()
196220
{
197-
return $this->getTable('catalog_product_index_price_bundle_sel_tmp');
221+
if ($this->tmpBundleSelectionTable === null) {
222+
$this->tmpBundleSelectionTable = $this->getTable('catalog_product_index_price_bundle_sel_temp');
223+
$this->getConnection()->createTemporaryTableLike(
224+
$this->tmpBundleSelectionTable,
225+
$this->getTable('catalog_product_index_price_bundle_sel_tmp'),
226+
true
227+
);
228+
}
229+
230+
return $this->tmpBundleSelectionTable;
198231
}
199232

200233
/**
@@ -204,7 +237,16 @@ private function getBundleSelectionTable()
204237
*/
205238
private function getBundleOptionTable()
206239
{
207-
return $this->getTable('catalog_product_index_price_bundle_opt_tmp');
240+
if ($this->tmpBundleOptionTable === null) {
241+
$this->tmpBundleOptionTable = $this->getTable('catalog_product_index_price_bundle_opt_temp');
242+
$this->getConnection()->createTemporaryTableLike(
243+
$this->tmpBundleOptionTable,
244+
$this->getTable('catalog_product_index_price_bundle_opt_tmp'),
245+
true
246+
);
247+
}
248+
249+
return $this->tmpBundleOptionTable;
208250
}
209251

210252
/**
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
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\Test\Unit\Model\ResourceModel\Indexer;
9+
10+
use Magento\Bundle\Model\ResourceModel\Indexer\Price;
11+
use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer;
12+
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BasePriceModifier;
13+
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructureFactory;
14+
use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Query\JoinAttributeProcessor;
15+
use Magento\Framework\App\ResourceConnection;
16+
use Magento\Framework\DB\Adapter\AdapterInterface;
17+
use Magento\Framework\EntityManager\MetadataPool;
18+
use Magento\Framework\Event\ManagerInterface;
19+
use Magento\Framework\Module\Manager;
20+
use PHPUnit\Framework\MockObject\MockObject;
21+
use PHPUnit\Framework\TestCase;
22+
23+
/**
24+
* Class to test Bundle products Price indexer resource model
25+
*/
26+
class PriceTest extends TestCase
27+
{
28+
/**
29+
* @var string
30+
*/
31+
private $connectionName = 'test_connection';
32+
33+
/**
34+
* @var ResourceConnection|MockObject
35+
*/
36+
private $resourceMock;
37+
38+
/**
39+
* @var AdapterInterface|MockObject
40+
*/
41+
private $connectionMock;
42+
43+
/**
44+
* @var Price
45+
*/
46+
private $priceModel;
47+
48+
/**
49+
* @inheritdoc
50+
*/
51+
protected function setUp(): void
52+
{
53+
parent::setUp();
54+
55+
$this->connectionMock = $this->createMock(AdapterInterface::class);
56+
$this->resourceMock = $this->createMock(ResourceConnection::class);
57+
$this->resourceMock->method('getConnection')
58+
->with($this->connectionName)
59+
->willReturn($this->connectionMock);
60+
$this->resourceMock->method('getTableName')->willReturnArgument(0);
61+
62+
/** @var IndexTableStructureFactory|MockObject $indexTableStructureFactory */
63+
$indexTableStructureFactory = $this->createMock(IndexTableStructureFactory::class);
64+
/** @var TableMaintainer|MockObject $tableMaintainer */
65+
$tableMaintainer = $this->createMock(TableMaintainer::class);
66+
/** @var MetadataPool|MockObject $metadataPool */
67+
$metadataPool = $this->createMock(MetadataPool::class);
68+
/** @var BasePriceModifier|MockObject $basePriceModifier */
69+
$basePriceModifier = $this->createMock(BasePriceModifier::class);
70+
/** @var JoinAttributeProcessor|MockObject $joinAttributeProcessor */
71+
$joinAttributeProcessor = $this->createMock(JoinAttributeProcessor::class);
72+
/** @var ManagerInterface|MockObject $eventManager */
73+
$eventManager = $this->createMock(ManagerInterface::class);
74+
/** @var Manager|MockObject $moduleManager */
75+
$moduleManager = $this->createMock(Manager::class);
76+
$fullReindexAction = false;
77+
78+
$this->priceModel = new Price(
79+
$indexTableStructureFactory,
80+
$tableMaintainer,
81+
$metadataPool,
82+
$this->resourceMock,
83+
$basePriceModifier,
84+
$joinAttributeProcessor,
85+
$eventManager,
86+
$moduleManager,
87+
$fullReindexAction,
88+
$this->connectionName
89+
);
90+
}
91+
92+
/**
93+
* Tests create Bundle Price temporary table
94+
*/
95+
public function testGetBundlePriceTable(): void
96+
{
97+
$expectedTmpTableName = 'catalog_product_index_price_bundle_temp';
98+
$expectedTableName = 'catalog_product_index_price_bundle_tmp';
99+
100+
$this->connectionMock->expects($this->once())
101+
->method('createTemporaryTableLike')
102+
->with($expectedTmpTableName, $expectedTableName, true);
103+
104+
$this->assertEquals(
105+
$expectedTmpTableName,
106+
$this->invokeMethodViaReflection('getBundlePriceTable')
107+
);
108+
}
109+
110+
/**
111+
* Tests create Bundle Selection Prices Index temporary table
112+
*/
113+
public function testGetBundleSelectionTable(): void
114+
{
115+
$expectedTmpTableName = 'catalog_product_index_price_bundle_sel_temp';
116+
$expectedTableName = 'catalog_product_index_price_bundle_sel_tmp';
117+
118+
$this->connectionMock->expects($this->once())
119+
->method('createTemporaryTableLike')
120+
->with($expectedTmpTableName, $expectedTableName, true);
121+
122+
$this->assertEquals(
123+
$expectedTmpTableName,
124+
$this->invokeMethodViaReflection('getBundleSelectionTable')
125+
);
126+
}
127+
128+
/**
129+
* Tests create Bundle Option Prices Index temporary table
130+
*/
131+
public function testGetBundleOptionTable(): void
132+
{
133+
$expectedTmpTableName = 'catalog_product_index_price_bundle_opt_temp';
134+
$expectedTableName = 'catalog_product_index_price_bundle_opt_tmp';
135+
136+
$this->connectionMock->expects($this->once())
137+
->method('createTemporaryTableLike')
138+
->with($expectedTmpTableName, $expectedTableName, true);
139+
140+
$this->assertEquals(
141+
$expectedTmpTableName,
142+
$this->invokeMethodViaReflection('getBundleOptionTable')
143+
);
144+
}
145+
146+
/**
147+
* Invoke private method via reflection
148+
*
149+
* @param string $methodName
150+
* @return string
151+
*/
152+
private function invokeMethodViaReflection(string $methodName): string
153+
{
154+
$method = new \ReflectionMethod(
155+
Price::class,
156+
$methodName
157+
);
158+
$method->setAccessible(true);
159+
160+
return (string)$method->invoke($this->priceModel);
161+
}
162+
}

app/code/Magento/Multishipping/Model/Cart/Controller/CartPlugin.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
use Magento\Customer\Api\AddressRepositoryInterface;
1313
use Magento\Framework\App\RequestInterface;
1414
use Magento\Framework\Exception\LocalizedException;
15-
use Magento\Multishipping\Model\Checkout\Type\Multishipping\State;
1615
use Magento\Multishipping\Model\DisableMultishipping;
1716
use Magento\Quote\Api\CartRepositoryInterface;
1817
use Magento\Quote\Model\Quote;
@@ -73,7 +72,7 @@ public function beforeDispatch(Cart $subject, RequestInterface $request)
7372
{
7473
/** @var Quote $quote */
7574
$quote = $this->checkoutSession->getQuote();
76-
if ($quote->isMultipleShippingAddresses()) {
75+
if ($quote->isMultipleShippingAddresses() || $this->isDisableMultishippingRequired($request, $quote)) {
7776
$this->disableMultishipping->execute($quote);
7877
foreach ($quote->getAllShippingAddresses() as $address) {
7978
$quote->removeAddress($address->getId());
@@ -111,4 +110,18 @@ private function isVirtualItemInQuote(Quote $quote): bool
111110

112111
return false;
113112
}
113+
114+
/**
115+
* Check if we have to disable multishipping mode depends on the request action name
116+
*
117+
* We should not disable multishipping mode if we are adding a new product item to the existing quote
118+
*
119+
* @param RequestInterface $request
120+
* @param Quote $quote
121+
* @return bool
122+
*/
123+
private function isDisableMultishippingRequired(RequestInterface $request, Quote $quote): bool
124+
{
125+
return $request->getActionName() !== "add" && $quote->getIsMultiShipping();
126+
}
114127
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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="StorefrontDisableMultishippingModeAfterRemoveItemOnBackToCartTest">
12+
<annotations>
13+
<features value="Multishipping"/>
14+
<stories value="Multishipping"/>
15+
<title value="Disable multishipping checkout on backing to cart after remove item"/>
16+
<description value="The cart page should display the proper subtotal after backing back to the cart from the multishipping checkout."/>
17+
<severity value="CRITICAL"/>
18+
<testCaseId value="MC-41594"/>
19+
<useCaseId value="MC-41464"/>
20+
<group value="multishipping"/>
21+
</annotations>
22+
<before>
23+
<createData entity="ApiCategory" stepKey="createCategory"/>
24+
<createData entity="_defaultProduct" stepKey="createSimpleProduct">
25+
<requiredEntity createDataKey="createCategory"/>
26+
</createData>
27+
<createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomerWithMultipleAddresses"/>
28+
</before>
29+
<after>
30+
<deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/>
31+
<deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
32+
<deleteData createDataKey="createCustomerWithMultipleAddresses" stepKey="deleteCustomer"/>
33+
</after>
34+
<actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer">
35+
<argument name="Customer" value="$createCustomerWithMultipleAddresses$"/>
36+
</actionGroup>
37+
38+
<actionGroup ref="StorefrontOpenProductEntityPageActionGroup" stepKey="openSimpleProductPage">
39+
<argument name="product" value="$createSimpleProduct$"/>
40+
</actionGroup>
41+
<grabTextFrom selector="{{StorefrontProductInfoMainSection.price}}" stepKey="grabPrice"/>
42+
<actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addSimpleProductToCart">
43+
<argument name="product" value="$createSimpleProduct$"/>
44+
<argument name="productCount" value="1"/>
45+
</actionGroup>
46+
<actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addSimpleProductToCartAgain">
47+
<argument name="product" value="$createSimpleProduct$"/>
48+
<argument name="productCount" value="2"/>
49+
</actionGroup>
50+
51+
<actionGroup ref="ClickViewAndEditCartFromMiniCartActionGroup" stepKey="goToShoppingCartFromMinicart"/>
52+
<actionGroup ref="StorefrontGoCheckoutWithMultipleAddressesActionGroup" stepKey="goCheckoutWithMultipleAddresses"/>
53+
<actionGroup ref="StorefrontRemoveProductOnCheckoutActionGroup" stepKey="removeFirstProductItemFromMultishipping"/>
54+
<actionGroup ref="StorefrontCartPageOpenActionGroup" stepKey="goBackToShoppingCartPage"/>
55+
<grabTextFrom selector="{{CheckoutCartProductSection.productSubtotalByName($createSimpleProduct.name$)}}" stepKey="grabSubtotal"/>
56+
<assertEquals stepKey="assertSubtotal" message="pass">
57+
<expectedResult type="variable">grabPrice</expectedResult>
58+
<actualResult type="variable">grabSubtotal</actualResult>
59+
</assertEquals>
60+
</test>
61+
</tests>

dev/tests/js/jasmine/tests/lib/mage/misc.test.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
* Copyright © Magento, Inc. All rights reserved.
33
* See COPYING.txt for license details.
44
*/
5-
5+
/* eslint-disable max-nested-callbacks */
66
define([
77
'mageUtils',
8-
'moment'
9-
], function (utils, moment) {
8+
'moment',
9+
'jquery'
10+
], function (utils, moment, $) {
1011
'use strict';
1112

1213
describe('mageUtils', function () {
@@ -681,5 +682,23 @@ define([
681682
}
682683
}
683684
});
685+
686+
it('Check ajaxSubmit method', function () {
687+
var options = {
688+
data: {}
689+
},
690+
config = {
691+
ajaxSaveType: 'default'
692+
},
693+
d = new $.Deferred();
694+
695+
spyOn($, 'ajax').and.callFake(function () {
696+
d.reject();
697+
698+
return d.promise();
699+
});
700+
utils.ajaxSubmit(options, config);
701+
expect($.ajax).toHaveBeenCalled();
702+
});
684703
});
685704
});

lib/web/mage/utils/misc.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,15 @@ define([
178178
}
179179
})
180180
.fail(function () {
181-
config.response.status(undefined);
182-
config.response.status(false);
183-
config.response.data({
184-
error: true,
185-
messages: 'Something went wrong.',
186-
t: t
187-
});
181+
if (config.response) {
182+
config.response.status(undefined);
183+
config.response.status(false);
184+
config.response.data({
185+
error: true,
186+
messages: 'Something went wrong.',
187+
t: t
188+
});
189+
}
188190
})
189191
.always(function () {
190192
if (!config.ignoreProcessEvents) {

0 commit comments

Comments
 (0)