Skip to content

Commit 632b3e5

Browse files
committed
Merge branch '2.4-develop' into refactor/mftf-customer
2 parents b5500f7 + 199d7c2 commit 632b3e5

File tree

141 files changed

+6211
-742
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

141 files changed

+6211
-742
lines changed

app/code/Magento/Analytics/Test/Mftf/Test/AdminAdvancedReportingNavigateMenuTest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
<argument name="submenuUiId" value="{{AdminMenuReportsBusinessIntelligenceAdvancedReporting.dataUiId}}"/>
3131
</actionGroup>
3232
<switchToNextTab stepKey="switchToNewTab"/>
33+
<waitForPageLoad stepKey="waitForAdvancedReportingPageLoad"/>
3334
<seeInCurrentUrl url="advancedreporting.rjmetrics.com/report" stepKey="seeAssertAdvancedReportingPageUrl"/>
3435
</test>
3536
</tests>

app/code/Magento/Bundle/view/base/web/js/price-bundle.js

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ define([
2828
controlContainer: 'dd', // should be eliminated
2929
priceFormat: {},
3030
isFixedPrice: false,
31-
optionTierPricesBlocksSelector: '#option-tier-prices-{1} [data-role="selection-tier-prices"]'
31+
optionTierPricesBlocksSelector: '#option-tier-prices-{1} [data-role="selection-tier-prices"]',
32+
isOptionsInitialized: false
3233
};
3334

3435
$.widget('mage.priceBundle', {
@@ -53,20 +54,37 @@ define([
5354
priceBox = $(this.options.priceBoxSelector, form),
5455
qty = $(this.options.qtyFieldSelector, form);
5556

56-
if (priceBox.data('magePriceBox') &&
57-
priceBox.priceBox('option') &&
58-
priceBox.priceBox('option').priceConfig
59-
) {
60-
if (priceBox.priceBox('option').priceConfig.optionTemplate) {
61-
this._setOption('optionTemplate', priceBox.priceBox('option').priceConfig.optionTemplate);
57+
this._updatePriceBox();
58+
priceBox.on('price-box-initialized', this._updatePriceBox.bind(this));
59+
options.on('change', this._onBundleOptionChanged.bind(this));
60+
qty.on('change', this._onQtyFieldChanged.bind(this));
61+
},
62+
63+
/**
64+
* Update price box config with bundle option prices
65+
* @private
66+
*/
67+
_updatePriceBox: function () {
68+
var form = this.element,
69+
options = $(this.options.productBundleSelector, form),
70+
priceBox = $(this.options.priceBoxSelector, form);
71+
72+
if (!this.options.isOptionsInitialized) {
73+
if (priceBox.data('magePriceBox') &&
74+
priceBox.priceBox('option') &&
75+
priceBox.priceBox('option').priceConfig
76+
) {
77+
if (priceBox.priceBox('option').priceConfig.optionTemplate) { //eslint-disable-line max-depth
78+
this._setOption('optionTemplate', priceBox.priceBox('option').priceConfig.optionTemplate);
79+
}
80+
this._setOption('priceFormat', priceBox.priceBox('option').priceConfig.priceFormat);
81+
priceBox.priceBox('setDefault', this.options.optionConfig.prices);
82+
this.options.isOptionsInitialized = true;
6283
}
63-
this._setOption('priceFormat', priceBox.priceBox('option').priceConfig.priceFormat);
64-
priceBox.priceBox('setDefault', this.options.optionConfig.prices);
84+
this._applyOptionNodeFix(options);
6585
}
66-
this._applyOptionNodeFix(options);
6786

68-
options.on('change', this._onBundleOptionChanged.bind(this));
69-
qty.on('change', this._onQtyFieldChanged.bind(this));
87+
return this;
7088
},
7189

7290
/**

app/code/Magento/Catalog/Api/Data/ProductRender/FormattedPriceInfoInterface.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public function getFinalPrice();
2929

3030
/**
3131
* Set the final price: usually it calculated as minimal price of the product
32+
*
3233
* Can be different depends on type of product
3334
*
3435
* @param string $finalPrice
@@ -39,6 +40,7 @@ public function setFinalPrice($finalPrice);
3940

4041
/**
4142
* Retrieve max price of a product
43+
*
4244
* E.g. for product with custom options is price with the most expensive custom option
4345
*
4446
* @return string
@@ -57,6 +59,7 @@ public function setMaxPrice($maxPrice);
5759

5860
/**
5961
* Retrieve the minimal price of the product or variation
62+
*
6063
* The minimal price is for example, the lowest price of all variations for complex product
6164
*
6265
* @return string
@@ -66,7 +69,7 @@ public function getMinimalPrice();
6669

6770
/**
6871
* Set max regular price
69-
* Max regular price is the same, as maximum price, except of excluding calculating special price and catalogules
72+
* Max regular price is the same, as maximum price, except of excluding calculating special price and catalog rules
7073
* in it
7174
*
7275
* @param string $maxRegularPrice
@@ -130,6 +133,7 @@ public function setMinimalPrice($minimalPrice);
130133

131134
/**
132135
* Regular price - is price of product without discounts and special price with taxes and fixed product tax
136+
*
133137
* Usually this price is corresponding to price in admin panel of product
134138
*
135139
* @return string

app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateCategoryWithInactiveIncludeInMenuTest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
<click selector="{{AdminCategoryBasicFieldSection.includeInMenuLabel}}" stepKey="disableIncludeInMenu"/>
4040
<scrollTo selector="{{AdminCategoryContentSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToContent"/>
4141
<click selector="{{AdminCategoryContentSection.sectionHeader}}" stepKey="selectContent"/>
42+
<scrollTo selector="{{AdminCategoryContentSection.description}}" x="0" y="-80" stepKey="scrollToDescription"/>
4243
<fillField selector="{{AdminCategoryContentSection.description}}" userInput="Updated category Description Fields" stepKey="fillUpdatedDescription"/>
4344
<scrollTo selector="{{AdminCategorySEOSection.SectionHeader}}" x="0" y="-80" stepKey="scrollToSearchEngineOptimization"/>
4445
<click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="selectSearchEngineOptimization"/>

app/code/Magento/Catalog/view/adminhtml/web/catalog/category/assign-products.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,29 @@ define([
5151
*/
5252
function categoryProductRowClick(grid, event) {
5353
var trElement = Event.findElement(event, 'tr'),
54-
isInput = Event.element(event).tagName === 'INPUT',
54+
eventElement = Event.element(event),
55+
isInputCheckbox = eventElement.tagName === 'INPUT' && eventElement.type === 'checkbox',
56+
isInputPosition = grid.targetElement &&
57+
grid.targetElement.tagName === 'INPUT' &&
58+
grid.targetElement.name === 'position',
5559
checked = false,
5660
checkbox = null;
5761

58-
if (trElement) {
62+
if (eventElement.tagName === 'LABEL' &&
63+
trElement.querySelector('#' + eventElement.htmlFor) &&
64+
trElement.querySelector('#' + eventElement.htmlFor).type === 'checkbox'
65+
) {
66+
event.stopPropagation();
67+
trElement.querySelector('#' + eventElement.htmlFor).trigger('click');
68+
69+
return;
70+
}
71+
72+
if (trElement && !isInputPosition) {
5973
checkbox = Element.getElementsBySelector(trElement, 'input');
6074

6175
if (checkbox[0]) {
62-
checked = isInput ? checkbox[0].checked : !checkbox[0].checked;
76+
checked = isInputCheckbox ? checkbox[0].checked : !checkbox[0].checked;
6377
gridJsObject.setCheckboxChecked(checkbox[0], checked);
6478
}
6579
}

app/code/Magento/Catalog/view/base/web/js/price-box.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ define([
4949

5050
box.on('reloadPrice', this.reloadPrice.bind(this));
5151
box.on('updatePrice', this.onUpdatePrice.bind(this));
52+
box.trigger('price-box-initialized');
5253
},
5354

5455
/**

app/code/Magento/Catalog/view/frontend/web/template/product/image_with_borders.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
-->
77
<span class="product-image-container" data-bind="style: {width: width + 'px'}">
88
<span class="product-image-wrapper" data-bind="style: {'padding-bottom': height/width*100 + '%'}">
9-
<img class="product-image-photo" data-bind="attr: {src: src, alt: alt}, style: {width: width + 'px', height: height + 'px'}" />
9+
<img class="product-image-photo" data-bind="attr: {src: src, alt: alt}, style: {width: 'auto', height: 'auto'}" />
1010
</span>
1111
</span>

app/code/Magento/CatalogGraphQl/Model/Resolver/Product/EntityIdToId.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public function resolve(
5353
$product = $value['model'];
5454

5555
$productId = $product->getData(
56-
$this->metadataPool->getMetadata(ProductInterface::class)->getLinkField()
56+
$this->metadataPool->getMetadata(ProductInterface::class)->getIdentifierField()
5757
);
5858

5959
return $productId;

app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/FieldSelection.php

Lines changed: 7 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
namespace Magento\CatalogGraphQl\Model\Resolver\Products\Query;
99

10-
use GraphQL\Language\AST\SelectionNode;
1110
use Magento\Framework\GraphQl\Query\FieldTranslator;
1211
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
1312

@@ -37,57 +36,18 @@ public function __construct(FieldTranslator $fieldTranslator)
3736
*/
3837
public function getProductsFieldSelection(ResolveInfo $resolveInfo): array
3938
{
40-
return $this->getProductFields($resolveInfo);
41-
}
39+
$productFields = $resolveInfo->getFieldSelection(1);
40+
$sectionNames = ['items', 'product'];
4241

43-
/**
44-
* Return field names for all requested product fields.
45-
*
46-
* @param ResolveInfo $info
47-
* @return string[]
48-
*/
49-
private function getProductFields(ResolveInfo $info): array
50-
{
5142
$fieldNames = [];
52-
foreach ($info->fieldNodes as $node) {
53-
if ($node->name->value !== 'products' && $node->name->value !== 'variants') {
54-
continue;
55-
}
56-
foreach ($node->selectionSet->selections as $selection) {
57-
if ($selection->name->value !== 'items' && $selection->name->value !== 'product') {
58-
continue;
59-
}
60-
$fieldNames[] = $this->collectProductFieldNames($selection, $fieldNames);
61-
}
62-
}
63-
if (!empty($fieldNames)) {
64-
$fieldNames = array_merge(...$fieldNames);
65-
}
66-
return $fieldNames;
67-
}
68-
69-
/**
70-
* Collect field names for each node in selection
71-
*
72-
* @param SelectionNode $selection
73-
* @param array $fieldNames
74-
* @return array
75-
*/
76-
private function collectProductFieldNames(SelectionNode $selection, array $fieldNames = []): array
77-
{
78-
foreach ($selection->selectionSet->selections as $itemSelection) {
79-
if ($itemSelection->kind === 'InlineFragment') {
80-
foreach ($itemSelection->selectionSet->selections as $inlineSelection) {
81-
if ($inlineSelection->kind === 'InlineFragment') {
82-
continue;
83-
}
84-
$fieldNames[] = $this->fieldTranslator->translate($inlineSelection->name->value);
43+
foreach ($sectionNames as $sectionName) {
44+
if (isset($productFields[$sectionName])) {
45+
foreach (array_keys($productFields[$sectionName]) as $fieldName) {
46+
$fieldNames[] = $this->fieldTranslator->translate($fieldName);
8547
}
86-
continue;
8748
}
88-
$fieldNames[] = $this->fieldTranslator->translate($itemSelection->name->value);
8949
}
9050

91-
return $fieldNames;
51+
return array_unique($fieldNames);
9252
}
9353
}

app/code/Magento/CatalogGraphQl/etc/schema.graphqls

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\
100100
created_at: String @doc(description: "Timestamp indicating when the product was created.")
101101
updated_at: String @doc(description: "Timestamp indicating when the product was updated.")
102102
country_of_manufacture: String @doc(description: "The product's country of origin.")
103-
type_id: String @doc(description: "One of simple, virtual, bundle, downloadable, grouped, or configurable.")
104-
websites: [Website] @doc(description: "An array of websites in which the product is available.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Websites")
103+
type_id: String @doc(description: "One of simple, virtual, bundle, downloadable, grouped, or configurable.") @deprecated(reason: "Use __typename instead.")
104+
websites: [Website] @doc(description: "An array of websites in which the product is available.") @deprecated(reason: "The field should not be used on the storefront.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Websites")
105105
product_links: [ProductLinksInterface] @doc(description: "An array of ProductLinks objects.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\BatchProductLinks")
106106
media_gallery_entries: [MediaGalleryEntry] @deprecated(reason: "Use product's `media_gallery` instead") @doc(description: "An array of MediaGalleryEntry objects.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\MediaGalleryEntries")
107107
price: ProductPrices @deprecated(reason: "Use price_range for product price information.") @doc(description: "A ProductPrices object, indicating the price of an item.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Price")
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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\CatalogSearch\Model\Search\FilterMapper;
9+
10+
use Magento\Catalog\Model\Product;
11+
use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver;
12+
use Magento\Eav\Model\Config as EavConfig;
13+
use Magento\Framework\DB\Select;
14+
use Magento\Framework\Search\Request\FilterInterface;
15+
16+
/**
17+
* Add stock status filter for each requested filter
18+
*/
19+
class CustomAttributeStockStatusFilter
20+
{
21+
/**
22+
* Suffix to append to filter name in order to generate stock status table alias for JOIN clause
23+
*/
24+
private const STOCK_STATUS_TABLE_ALIAS_SUFFIX = '_stock_index';
25+
/**
26+
* Attribute types to apply
27+
*/
28+
private const TARGET_ATTRIBUTE_TYPES = [
29+
'select',
30+
'multiselect'
31+
];
32+
/**
33+
* @var EavConfig
34+
*/
35+
private $eavConfig;
36+
/**
37+
* @var AliasResolver
38+
*/
39+
private $aliasResolver;
40+
/**
41+
* @var StockStatusQueryBuilder|null
42+
*/
43+
private $stockStatusQueryBuilder;
44+
45+
/**
46+
* @param EavConfig $eavConfig
47+
* @param AliasResolver $aliasResolver
48+
* @param StockStatusQueryBuilder $stockStatusQueryBuilder
49+
*/
50+
public function __construct(
51+
EavConfig $eavConfig,
52+
AliasResolver $aliasResolver,
53+
StockStatusQueryBuilder $stockStatusQueryBuilder
54+
) {
55+
$this->eavConfig = $eavConfig;
56+
$this->aliasResolver = $aliasResolver;
57+
$this->stockStatusQueryBuilder = $stockStatusQueryBuilder;
58+
}
59+
60+
/**
61+
* Apply stock status filter to provided filter
62+
*
63+
* @param Select $select
64+
* @param mixed $values
65+
* @param FilterInterface[] $filters
66+
* @return Select
67+
*/
68+
public function apply(Select $select, $values = null, FilterInterface ...$filters): Select
69+
{
70+
$select = clone $select;
71+
foreach ($filters as $filter) {
72+
if ($this->isApplicable($filter)) {
73+
$mainTableAlias = $this->aliasResolver->getAlias($filter);
74+
$stockTableAlias = $mainTableAlias . self::STOCK_STATUS_TABLE_ALIAS_SUFFIX;
75+
$select = $this->stockStatusQueryBuilder->apply(
76+
$select,
77+
$mainTableAlias,
78+
$stockTableAlias,
79+
'source_id',
80+
$values
81+
);
82+
}
83+
}
84+
return $select;
85+
}
86+
87+
/**
88+
* Check if stock status filter is applicable to provided filter
89+
*
90+
* @param FilterInterface $filter
91+
* @return bool
92+
*/
93+
private function isApplicable(FilterInterface $filter): bool
94+
{
95+
$attribute = $this->eavConfig->getAttribute(Product::ENTITY, $filter->getField());
96+
return $attribute
97+
&& $filter->getType() === FilterInterface::TYPE_TERM
98+
&& in_array($attribute->getFrontendInput(), self::TARGET_ATTRIBUTE_TYPES, true);
99+
}
100+
}

0 commit comments

Comments
 (0)