Skip to content

Commit fa34dbf

Browse files
committed
Merge remote-tracking branch 'origin/2.3-develop' into MQE-2202
2 parents 7bb1524 + 4aea58a commit fa34dbf

File tree

31 files changed

+817
-134
lines changed

31 files changed

+817
-134
lines changed

app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/AttributeOptionProvider.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace Magento\CatalogGraphQl\DataProvider\Product\LayeredNavigation;
99

1010
use Magento\Framework\App\ResourceConnection;
11+
use Magento\Store\Model\Store;
1112

1213
/**
1314
* Fetch product attribute option data including attribute info
@@ -41,16 +42,18 @@ public function __construct(ResourceConnection $resourceConnection)
4142
* Get option data. Return list of attributes with option data
4243
*
4344
* @param array $optionIds
45+
* @param int|null $storeId
4446
* @param array $attributeCodes
4547
* @return array
4648
* @throws \Zend_Db_Statement_Exception
4749
*/
48-
public function getOptions(array $optionIds, array $attributeCodes = []): array
50+
public function getOptions(array $optionIds, ?int $storeId, array $attributeCodes = []): array
4951
{
5052
if (!$optionIds) {
5153
return [];
5254
}
5355

56+
$storeId = $storeId ?: Store::DEFAULT_STORE_ID;
5457
$connection = $this->resourceConnection->getConnection();
5558
$select = $connection->select()
5659
->from(
@@ -70,9 +73,21 @@ public function getOptions(array $optionIds, array $attributeCodes = []): array
7073
['option_value' => $this->resourceConnection->getTableName('eav_attribute_option_value')],
7174
'options.option_id = option_value.option_id',
7275
[
73-
'option_label' => 'option_value.value',
7476
'option_id' => 'option_value.option_id',
7577
]
78+
)->joinLeft(
79+
['option_value_store' => $this->resourceConnection->getTableName('eav_attribute_option_value')],
80+
"options.option_id = option_value_store.option_id AND option_value_store.store_id = {$storeId}",
81+
[
82+
'option_label' => $connection->getCheckSql(
83+
'option_value_store.value_id > 0',
84+
'option_value_store.value',
85+
'option_value.value'
86+
)
87+
]
88+
)->where(
89+
'a.attribute_id = options.attribute_id AND option_value.store_id = ?',
90+
Store::DEFAULT_STORE_ID
7691
);
7792

7893
$select->where('option_value.option_id IN (?)', $optionIds);

app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Builder/Attribute.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public function __construct(
7171
*/
7272
public function build(AggregationInterface $aggregation, ?int $storeId): array
7373
{
74-
$attributeOptions = $this->getAttributeOptions($aggregation);
74+
$attributeOptions = $this->getAttributeOptions($aggregation, $storeId);
7575

7676
// build layer per attribute
7777
$result = [];
@@ -133,10 +133,11 @@ private function isBucketEmpty(?BucketInterface $bucket): bool
133133
* Get list of attributes with options
134134
*
135135
* @param AggregationInterface $aggregation
136+
* @param int|null $storeId
136137
* @return array
137138
* @throws \Zend_Db_Statement_Exception
138139
*/
139-
private function getAttributeOptions(AggregationInterface $aggregation): array
140+
private function getAttributeOptions(AggregationInterface $aggregation, ?int $storeId): array
140141
{
141142
$attributeOptionIds = [];
142143
$attributes = [];
@@ -154,6 +155,6 @@ function (AggregationValueInterface $value) {
154155
return [];
155156
}
156157

157-
return $this->attributeOptionProvider->getOptions(\array_merge(...$attributeOptionIds), $attributes);
158+
return $this->attributeOptionProvider->getOptions(\array_merge(...$attributeOptionIds), $storeId, $attributes);
158159
}
159160
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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="StorefrontApplyDiscountCouponDuringCheckoutTest">
12+
<annotations>
13+
<features value="OnePageCheckout"/>
14+
<stories value="OnePageCheckout with Promo Code"/>
15+
<title value="Storefront apply promo code during checkout test"/>
16+
<description value="Apply promo code during checkout for physical product"/>
17+
<severity value="CRITICAL"/>
18+
<testCaseId value="MC-13212"/>
19+
<group value="checkout"/>
20+
</annotations>
21+
<before>
22+
<!-- Create simple product -->
23+
<createData entity="SimpleProduct2" stepKey="createProduct">
24+
<field key="price">10.00</field>
25+
</createData>
26+
27+
<!-- Create cart price rule -->
28+
<createData entity="ActiveSalesRuleForNotLoggedIn" stepKey="createCartPriceRule"/>
29+
<createData entity="SimpleSalesRuleCoupon" stepKey="createCouponForCartPriceRule">
30+
<requiredEntity createDataKey="createCartPriceRule"/>
31+
</createData>
32+
33+
<!-- Login as admin -->
34+
<actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/>
35+
</before>
36+
<after>
37+
<!-- Delete simple product -->
38+
<deleteData createDataKey="createProduct" stepKey="deleteProduct"/>
39+
<!-- Delete sales rule -->
40+
<deleteData createDataKey="createCartPriceRule" stepKey="deleteCartPriceRule"/>
41+
<actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderFilters"/>
42+
<!-- Admin log out -->
43+
<actionGroup ref="logout" stepKey="logout"/>
44+
</after>
45+
46+
<!-- Go to Storefront as Guest and add simple product to cart -->
47+
<actionGroup ref="AddSimpleProductToCartActionGroup" stepKey="addProductToCart">
48+
<argument name="product" value="$createProduct$"/>
49+
</actionGroup>
50+
51+
<!-- Go to Checkout -->
52+
<actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/>
53+
54+
<!-- Fill all required fields with valid data and select Flat Rate, price = 5, shipping -->
55+
<actionGroup ref="GuestCheckoutFillNewShippingAddressActionGroup" stepKey="guestCheckoutFillingShipping">
56+
<argument name="customer" value="CustomerEntityOne"/>
57+
<argument name="address" value="CustomerAddressSimple"/>
58+
</actionGroup>
59+
<waitForPageLoad stepKey="waitForShippingMethods"/>
60+
<actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" stepKey="selectShippingMethod"/>
61+
<actionGroup ref="StorefrontCheckoutForwardFromShippingStep" stepKey="goToPaymentStep"/>
62+
63+
<!-- Click Apply Discount Code: section is expanded. Input promo code, apply and see success message -->
64+
<actionGroup ref="StorefrontApplyDiscountCodeActionGroup" stepKey="applyCoupon">
65+
<argument name="discountCode" value="$createCouponForCartPriceRule.code$"/>
66+
</actionGroup>
67+
68+
<!-- Apply button is disappeared -->
69+
<dontSeeElement selector="{{DiscountSection.ApplyCodeBtn}}" stepKey="dontSeeApplyButton"/>
70+
71+
<!-- Cancel coupon button is appeared -->
72+
<waitForElementVisible selector="{{DiscountSection.CancelCouponBtn}}" stepKey="waitCancelButtonAppears"/>
73+
<seeElement selector="{{DiscountSection.CancelCouponBtn}}" stepKey="seeCancelCouponButton"/>
74+
75+
<!-- Order summary contains information about applied code -->
76+
<waitForElementVisible selector="{{CheckoutPaymentSection.discount}}" stepKey="waitForDiscountCouponInSummaryBlock"/>
77+
<seeElement selector="{{CheckoutPaymentSection.discount}}" stepKey="seeDiscountCouponInSummaryBlock"/>
78+
<see selector="{{CheckoutPaymentSection.discountPrice}}" userInput="-$5.00" stepKey="seeDiscountPrice"/>
79+
80+
<!-- Select payment solution -->
81+
<actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="clickCheckMoneyOrderPayment"/>
82+
83+
<!-- Place Order: order is successfully placed -->
84+
<actionGroup ref="ClickPlaceOrderActionGroup" stepKey="clickPlaceOrder"/>
85+
<grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber}}" stepKey="grabOrderNumber"/>
86+
87+
<!-- Verify total on order page -->
88+
<actionGroup ref="OpenOrderByIdActionGroup" stepKey="filterOrderById">
89+
<argument name="orderId" value="$grabOrderNumber"/>
90+
</actionGroup>
91+
<scrollTo selector="{{AdminOrderTotalSection.grandTotal}}" stepKey="scrollToOrderTotalSection"/>
92+
<see selector="{{AdminOrderTotalSection.grandTotal}}" userInput="$createProduct.price$" stepKey="checkTotal"/>
93+
</test>
94+
</tests>

app/code/Magento/Checkout/Test/Mftf/Test/StorefrontApplyPromoCodeDuringCheckoutTest.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@
1212
<annotations>
1313
<features value="OnePageCheckout"/>
1414
<stories value="OnePageCheckout with Promo Code"/>
15-
<title value="Storefront apply promo code during checkout test"/>
15+
<title value="DEPRECATED Storefront apply promo code during checkout test"/>
1616
<description value="Apply promo code during checkout for physical product"/>
1717
<severity value="CRITICAL"/>
1818
<testCaseId value="MC-13212"/>
1919
<group value="checkout"/>
20+
<skip>
21+
<issueId value="DEPRECATED">Use StorefrontApplyDiscountCouponDuringCheckoutTest instead</issueId>
22+
</skip>
2023
</annotations>
2124
<before>
2225
<!-- Create simple product -->

app/code/Magento/Checkout/view/frontend/web/js/region-updater.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,10 @@ define([
157157
regionInput = $(this.options.regionInputId),
158158
postcode = $(this.options.postcodeId),
159159
label = regionList.parent().siblings('label'),
160-
container = regionList.parents('div.field');
160+
container = regionList.parents('div.field'),
161+
regionsEntries,
162+
regionId,
163+
regionData;
161164

162165
this._clearError();
163166
this._checkRegionRequired(country);
@@ -168,8 +171,14 @@ define([
168171
// Populate state/province dropdown list if available or use input box
169172
if (this.options.regionJson[country]) {
170173
this._removeSelectOptions(regionList);
171-
$.each(this.options.regionJson[country], $.proxy(function (key, value) {
172-
this._renderSelectOption(regionList, key, value);
174+
regionsEntries = Object.entries(this.options.regionJson[country]);
175+
regionsEntries.sort(function (a, b) {
176+
return a[1].name > b[1].name ? 1 : -1;
177+
});
178+
$.each(regionsEntries, $.proxy(function (key, value) {
179+
regionId = value[0];
180+
regionData = value[1];
181+
this._renderSelectOption(regionList, regionId, regionData);
173182
}, this));
174183

175184
if (this.currentRegionOption) {

app/code/Magento/Cms/Test/Mftf/Section/TinyMCESection.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@
110110
<element name="PageSize" type="input" selector="input[name='parameters[page_size]']"/>
111111
<element name="ProductAttribute" type="multiselect" selector="select[name='parameters[show_attributes][]']" />
112112
<element name="ButtonToShow" type="multiselect" selector="select[name='parameters[show_buttons][]']"/>
113+
<element name="InputAnchorCustomText" type="input" selector="input[name='parameters[anchor_text]']"/>
114+
<element name="InputAnchorCustomTitle" type="input" selector="input[name='parameters[title]']"/>
113115
<!--Compare on Storefront-->
114116
<element name="ProductName" type="text" selector=".product.name.product-item-name" />
115117
<element name="CompareBtn" type="button" selector=".action.tocompare"/>

app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
67
namespace Magento\Elasticsearch\Elasticsearch5\Model\Client;
78

89
use Magento\Framework\Exception\LocalizedException;
@@ -48,8 +49,10 @@ public function __construct(
4849
$options = [],
4950
$elasticsearchClient = null
5051
) {
51-
if (empty($options['hostname']) || ((!empty($options['enableAuth']) &&
52-
($options['enableAuth'] == 1)) && (empty($options['username']) || empty($options['password'])))) {
52+
if (empty($options['hostname'])
53+
|| ((!empty($options['enableAuth']) && ($options['enableAuth'] == 1))
54+
&& (empty($options['username']) || empty($options['password'])))
55+
) {
5356
throw new LocalizedException(
5457
__('The search failed because of a search engine misconfiguration.')
5558
);
@@ -302,7 +305,15 @@ public function addFieldsMapping(array $fields, $index, $entityType)
302305
]
303306
),
304307
],
305-
]
308+
],
309+
[
310+
'integer_mapping' => [
311+
'match_mapping_type' => 'long',
312+
'mapping' => [
313+
'type' => 'integer',
314+
],
315+
],
316+
],
306317
],
307318
],
308319
],

app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
67
namespace Magento\Elasticsearch\Model\Adapter\BatchDataMapper;
78

89
use Magento\CatalogSearch\Model\Indexer\Fulltext\Action\DataProvider;
@@ -74,26 +75,30 @@ class ProductDataMapper implements BatchDataMapperInterface
7475
private $attributesExcludedFromMerge = [
7576
'status',
7677
'visibility',
77-
'tax_class_id'
78+
'tax_class_id',
7879
];
7980

8081
/**
8182
* @var string[]
8283
*/
8384
private $sortableAttributesValuesToImplode = [
84-
'name'
85+
'name',
8586
];
8687

8788
/**
88-
* Construction for DocumentDataMapper
89-
*
89+
* @var string[]
90+
*/
91+
private $filterableAttributeTypes;
92+
93+
/**
9094
* @param Builder $builder
9195
* @param FieldMapperInterface $fieldMapper
9296
* @param DateFieldType $dateFieldType
9397
* @param AdditionalFieldsProviderInterface $additionalFieldsProvider
9498
* @param DataProvider $dataProvider
9599
* @param array $excludedAttributes
96100
* @param array $sortableAttributesValuesToImplode
101+
* @param array $filterableAttributeTypes
97102
*/
98103
public function __construct(
99104
Builder $builder,
@@ -102,7 +107,8 @@ public function __construct(
102107
AdditionalFieldsProviderInterface $additionalFieldsProvider,
103108
DataProvider $dataProvider,
104109
array $excludedAttributes = [],
105-
array $sortableAttributesValuesToImplode = []
110+
array $sortableAttributesValuesToImplode = [],
111+
array $filterableAttributeTypes = []
106112
) {
107113
$this->builder = $builder;
108114
$this->fieldMapper = $fieldMapper;
@@ -115,6 +121,7 @@ public function __construct(
115121
$this->additionalFieldsProvider = $additionalFieldsProvider;
116122
$this->dataProvider = $dataProvider;
117123
$this->attributeOptionsCache = [];
124+
$this->filterableAttributeTypes = $filterableAttributeTypes;
118125
}
119126

120127
/**
@@ -212,7 +219,7 @@ private function convertAttribute(Attribute $attribute, array $attributeValues,
212219
if ($retrievedValue !== null) {
213220
$productAttributes[$attribute->getAttributeCode()] = $retrievedValue;
214221

215-
if ($attribute->getIsSearchable()) {
222+
if ($this->isAttributeLabelsShouldBeMapped($attribute)) {
216223
$attributeLabels = $this->getValuesLabels($attribute, $attributeValues, $storeId);
217224
$retrievedLabel = $this->retrieveFieldValue($attributeLabels);
218225
if ($retrievedLabel) {
@@ -224,6 +231,26 @@ private function convertAttribute(Attribute $attribute, array $attributeValues,
224231
return $productAttributes;
225232
}
226233

234+
/**
235+
* Check if an attribute has one of the next storefront properties enabled for mapping labels:
236+
* - "Use in Search" (is_searchable)
237+
* - "Visible in Advanced Search" (is_visible_in_advanced_search)
238+
* - "Use in Layered Navigation" (is_filterable)
239+
* - "Use in Search Results Layered Navigation" (is_filterable_in_search)
240+
*
241+
* @param Attribute $attribute
242+
* @return bool
243+
*/
244+
private function isAttributeLabelsShouldBeMapped(Attribute $attribute): bool
245+
{
246+
return (
247+
$attribute->getIsSearchable()
248+
|| $attribute->getIsVisibleInAdvancedSearch()
249+
|| $attribute->getIsFilterable()
250+
|| $attribute->getIsFilterableInSearch()
251+
);
252+
}
253+
227254
/**
228255
* Prepare attribute values.
229256
*
@@ -249,6 +276,15 @@ private function prepareAttributeValues(
249276
$attributeValues = $this->prepareMultiselectValues($attributeValues);
250277
}
251278

279+
if (in_array($attribute->getFrontendInput(), $this->filterableAttributeTypes)) {
280+
$attributeValues = array_map(
281+
function (string $valueId) {
282+
return (int)$valueId;
283+
},
284+
$attributeValues
285+
);
286+
}
287+
252288
if ($this->isAttributeDate($attribute)) {
253289
foreach ($attributeValues as $key => $attributeValue) {
254290
$attributeValues[$key] = $this->dateFieldType->formatDate($storeId, $attributeValue);

0 commit comments

Comments
 (0)