Skip to content

Commit cdb4d36

Browse files
Chhandak.BaruaChhandak.Barua
authored andcommitted
Merge branch '2.4-develop' of https://github.com/magento-l3/magento2ce into ACP2E-2515
2 parents 5e9ba20 + 50c513f commit cdb4d36

File tree

73 files changed

+5010
-909
lines changed

Some content is hidden

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

73 files changed

+5010
-909
lines changed

app/code/Magento/Catalog/Helper/Product/View.php

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,7 @@ private function preparePageMetadata(ResultPage $resultPage, $product)
130130
$pageConfig->setKeywords($product->getName());
131131
}
132132

133-
$description = $product->getMetaDescription();
134-
if ($description) {
135-
$pageConfig->setDescription($description);
136-
} else {
137-
$productDescription = is_string($product->getDescription()) ?
138-
$this->string->substr(strip_tags($product->getDescription()), 0, 255) : '';
139-
$pageConfig->setDescription($productDescription);
140-
}
133+
$pageConfig->setDescription($product->getMetaDescription());
141134

142135
if ($this->_catalogProduct->canUseCanonicalTag()) {
143136
$pageConfig->addRemotePageAsset(

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@
2626
</argument>
2727
</arguments>
2828
</virtualType>
29+
<virtualType name="Magento\Framework\GraphQl\Config\Data" type="Magento\Framework\Config\Data">
30+
<arguments>
31+
<argument name="cacheTags" xsi:type="array">
32+
<!-- Note: Because of DynamicAttributeReaders, this cache needs to be cleaned when attributes change-->
33+
<item name="EAV" xsi:type="string">EAV</item>
34+
</argument>
35+
</arguments>
36+
</virtualType>
2937
<type name="Magento\Framework\GraphQl\Query\FieldTranslator">
3038
<arguments>
3139
<argument name="translationMap" xsi:type="array">
@@ -97,6 +105,14 @@
97105
<type name="Magento\Framework\Search\Request\Config\FilesystemReader">
98106
<plugin name="productAttributesDynamicFields" type="Magento\CatalogGraphQl\Plugin\Search\Request\ConfigReader" />
99107
</type>
108+
<type name="Magento\Framework\Search\Request\Config">
109+
<arguments>
110+
<argument name="cacheTags" xsi:type="array">
111+
<!-- Note: We have to add EAV to the cache tags because productAttributesDynamicFields uses EAV -->
112+
<item name="EAV" xsi:type="string">EAV</item>
113+
</argument>
114+
</arguments>
115+
</type>
100116

101117
<preference type="Magento\CatalogGraphQl\Model\Resolver\Product\Price\Provider" for="Magento\CatalogGraphQl\Model\Resolver\Product\Price\ProviderInterface"/>
102118

app/code/Magento/CatalogRuleConfigurable/Plugin/CatalogRule/Model/Rule/Validation.php

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,19 @@ public function afterValidate(Rule $rule, $validateResult, DataObject $product)
4949
{
5050
if (!$validateResult && ($configurableProducts = $this->configurable->getParentIdsByChild($product->getId()))) {
5151
foreach ($configurableProducts as $configurableProductId) {
52-
$configurableProduct = $this->productRepository->getById(
53-
$configurableProductId,
54-
false,
55-
$product->getStoreId()
56-
);
57-
$validateResult = $rule->getConditions()->validate($configurableProduct);
58-
// If any of configurable product is valid for current rule, then their sub-product must be valid too
59-
if ($validateResult) {
60-
break;
52+
try {
53+
$configurableProduct = $this->productRepository->getById(
54+
$configurableProductId,
55+
false,
56+
$product->getStoreId()
57+
);
58+
$validateResult = $rule->getConditions()->validate($configurableProduct);
59+
//If any of configurable product is valid for current rule, then their sub-product must be valid too
60+
if ($validateResult) {
61+
break;
62+
}
63+
} catch (\Exception $e) {
64+
continue;
6165
}
6266
}
6367
}

app/code/Magento/CatalogRuleConfigurable/Test/Unit/Plugin/CatalogRule/Model/Rule/ValidationTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,32 @@ protected function setUp(): void
6666
);
6767
}
6868

69+
/**
70+
* @return void
71+
*/
72+
public function testAfterValidateConfigurableProductException(): void
73+
{
74+
$validationResult = false;
75+
$parentsIds = [2];
76+
$productId = 1;
77+
78+
$this->productMock->expects($this->once())
79+
->method('getId')
80+
->willReturn($productId);
81+
$this->configurableMock->expects($this->once())
82+
->method('getParentIdsByChild')
83+
->with($productId)
84+
->willReturn($parentsIds);
85+
$this->productRepositoryMock->expects($this->once())
86+
->method('getById')
87+
->willThrowException(new \Exception('Faulty configurable product'));
88+
89+
$this->assertSame(
90+
$validationResult,
91+
$this->validation->afterValidate($this->ruleMock, $validationResult, $this->productMock)
92+
);
93+
}
94+
6995
/**
7096
* @param $parentsIds
7197
* @param $validationResult
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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\ConfigurableProduct\Model;
9+
10+
use Magento\Framework\Pricing\Adjustment\CalculatorInterface;
11+
use Magento\Framework\Pricing\PriceCurrencyInterface;
12+
use Magento\Framework\App\ResourceConnection;
13+
14+
class ConfigurableMaxPriceCalculator
15+
{
16+
/**
17+
* @var CalculatorInterface
18+
*/
19+
private $calculator;
20+
21+
/**
22+
* @var PriceCurrencyInterface
23+
*/
24+
private $priceCurrency;
25+
26+
/**
27+
* @var ResourceConnection
28+
*/
29+
private $resourceConnection;
30+
31+
/**
32+
* @param CalculatorInterface $calculator
33+
* @param PriceCurrencyInterface $priceCurrency
34+
* @param ResourceConnection $resourceConnection
35+
*/
36+
public function __construct(
37+
CalculatorInterface $calculator,
38+
PriceCurrencyInterface $priceCurrency,
39+
ResourceConnection $resourceConnection
40+
) {
41+
$this->calculator = $calculator;
42+
$this->priceCurrency = $priceCurrency;
43+
$this->resourceConnection = $resourceConnection;
44+
}
45+
46+
/**
47+
* Get the maximum price of a configurable product.
48+
*
49+
* @param int $productId
50+
* @return float
51+
*/
52+
public function getMaxPriceForConfigurableProduct($productId)
53+
{
54+
$connection = $this->resourceConnection->getConnection();
55+
$superLinkTable = $this->resourceConnection->getTableName('catalog_product_super_link');
56+
$catalogProductTable = $this->resourceConnection->getTableName('catalog_product_entity');
57+
$priceIndexTable = $this->resourceConnection->getTableName('catalog_product_index_price');
58+
$select = $connection->select()
59+
->from(['sl' => $superLinkTable], [])
60+
->join(['pe' => $catalogProductTable], 'sl.product_id = pe.entity_id', [])
61+
->join(['ip' => $priceIndexTable], 'pe.entity_id = ip.entity_id', ['max_price' => 'MAX(ip.final_price)'])
62+
->where('sl.parent_id = ?', $productId);
63+
$result = $connection->fetchRow($select);
64+
65+
if ($result && isset($result['max_price'])) {
66+
return $result['max_price'];
67+
}
68+
69+
// Return a default value or handle the case where there's no max price
70+
return 0.00;
71+
}
72+
}

app/code/Magento/ConfigurableProduct/Pricing/Price/ConfigurableRegularPrice.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
namespace Magento\ConfigurableProduct\Pricing\Price;
88

99
use Magento\Catalog\Model\Product;
10+
use Magento\ConfigurableProduct\Model\ConfigurableMaxPriceCalculator;
1011
use Magento\Framework\App\ObjectManager;
1112
use Magento\Framework\ObjectManager\ResetAfterRequestInterface;
1213
use Magento\Framework\Pricing\Price\AbstractPrice;
14+
use Magento\Framework\Pricing\SaleableInterface;
1315

1416
/**
1517
* Class RegularPrice
@@ -53,12 +55,18 @@ class ConfigurableRegularPrice extends AbstractPrice implements
5355
*/
5456
private $lowestPriceOptionsProvider;
5557

58+
/**
59+
* @var ConfigurableMaxPriceCalculator
60+
*/
61+
private $configurableMaxPriceCalculator;
62+
5663
/**
5764
* @param \Magento\Framework\Pricing\SaleableInterface $saleableItem
5865
* @param float $quantity
5966
* @param \Magento\Framework\Pricing\Adjustment\CalculatorInterface $calculator
6067
* @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency
6168
* @param PriceResolverInterface $priceResolver
69+
* @param ConfigurableMaxPriceCalculator $configurableMaxPriceCalculator
6270
* @param LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider
6371
*/
6472
public function __construct(
@@ -67,12 +75,14 @@ public function __construct(
6775
\Magento\Framework\Pricing\Adjustment\CalculatorInterface $calculator,
6876
\Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency,
6977
PriceResolverInterface $priceResolver,
78+
ConfigurableMaxPriceCalculator $configurableMaxPriceCalculator,
7079
LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider = null
7180
) {
7281
parent::__construct($saleableItem, $quantity, $calculator, $priceCurrency);
7382
$this->priceResolver = $priceResolver;
7483
$this->lowestPriceOptionsProvider = $lowestPriceOptionsProvider ?:
7584
ObjectManager::getInstance()->get(LowestPriceOptionsProviderInterface::class);
85+
$this->configurableMaxPriceCalculator = $configurableMaxPriceCalculator;
7686
}
7787

7888
/**
@@ -184,4 +194,28 @@ public function _resetState(): void
184194
{
185195
$this->values = [];
186196
}
197+
198+
/**
199+
* Check whether Configurable Product have more than one children products
200+
*
201+
* @param SaleableInterface $product
202+
* @return bool
203+
*/
204+
public function isChildProductsOfEqualPrices(SaleableInterface $product): bool
205+
{
206+
$minPrice = $this->getMinRegularAmount()->getValue();
207+
$final_price = $product->getFinalPrice();
208+
$productId = $product->getId();
209+
if ($final_price < $minPrice) {
210+
return false;
211+
}
212+
$attributes = $product->getTypeInstance()->getConfigurableAttributes($product);
213+
$items = $attributes->getItems();
214+
$options = reset($items);
215+
$maxPrice = $this->configurableMaxPriceCalculator->getMaxPriceForConfigurableProduct($productId);
216+
if ($maxPrice == 0) {
217+
$maxPrice = $this->getMaxRegularAmount()->getValue();
218+
}
219+
return (count($options->getOptions()) > 1) && $minPrice == $maxPrice;
220+
}
187221
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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="CreateConfigurableProductWithSamePriceActionGroup">
12+
<annotations>
13+
<description>Goes to the Admin Product grid page.
14+
Create a Configurable Product using the default Product Options with identical pricing.</description>
15+
</annotations>
16+
<arguments>
17+
<argument name="product" defaultValue="_defaultProduct"/>
18+
<argument name="category" defaultValue="_defaultCategory"/>
19+
</arguments>
20+
21+
<!-- fill in basic configurable product values -->
22+
<amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/>
23+
<waitForPageLoad time="30" stepKey="wait1"/>
24+
<click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickOnAddProductToggle"/>
25+
<click selector="{{AdminProductGridActionSection.addConfigurableProduct}}"
26+
stepKey="clickOnAddConfigurableProduct"/>
27+
<fillField userInput="{{product.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/>
28+
<fillField userInput="{{product.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/>
29+
<fillField userInput="{{product.price}}" selector="{{AdminProductFormSection.productPrice}}"
30+
stepKey="fillPrice"/>
31+
<fillField userInput="{{product.quantity}}" selector="{{AdminProductFormSection.productQuantity}}"
32+
stepKey="fillQuantity"/>
33+
<searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}"
34+
parameterArray="[{{category.name}}]" stepKey="fillCategory"/>
35+
<selectOption userInput="{{product.visibility}}" selector="{{AdminProductFormSection.visibility}}"
36+
stepKey="fillVisibility"/>
37+
<click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/>
38+
<fillField userInput="{{product.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}"
39+
stepKey="fillUrlKey"/>
40+
41+
<!-- create configurations for colors the product is available in -->
42+
<click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}"
43+
stepKey="clickOnCreateConfigurations"/>
44+
<click selector="{{AdminCreateProductConfigurationsPanel.createNewAttribute}}" stepKey="clickOnNewAttribute"/>
45+
<waitForPageLoad stepKey="waitForIFrame"/>
46+
<switchToIFrame selector="{{AdminNewAttributePanel.newAttributeIFrame}}" stepKey="switchToNewAttributeIFrame"/>
47+
<fillField selector="{{AdminNewAttributePanel.defaultLabel}}"
48+
userInput="{{colorProductAttribute.default_label}}"
49+
stepKey="fillDefaultLabel"/>
50+
<click selector="{{AdminNewAttributePanel.saveAttribute}}" stepKey="clickOnNewAttributePanel"/>
51+
<waitForPageLoad stepKey="waitForSaveAttribute"/>
52+
<switchToIFrame stepKey="switchOutOfIFrame"/>
53+
<waitForPageLoad stepKey="waitForFilters"/>
54+
<click selector="{{AdminCreateProductConfigurationsPanel.filters}}" stepKey="clickOnFilters"/>
55+
<fillField userInput="{{colorProductAttribute.default_label}}"
56+
selector="{{AdminCreateProductConfigurationsPanel.attributeCode}}"
57+
stepKey="fillFilterAttributeCodeField"/>
58+
<click selector="{{AdminCreateProductConfigurationsPanel.applyFilters}}" stepKey="clickApplyFiltersButton"/>
59+
<click selector="{{AdminCreateProductConfigurationsPanel.firstCheckbox}}" stepKey="clickOnFirstCheckbox"/>
60+
<click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton1"/>
61+
<waitForElementVisible selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}"
62+
stepKey="waitCreateNewValueAppears"/>
63+
<click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue1"/>
64+
<fillField userInput="{{colorProductAttribute1.name}}"
65+
selector="{{AdminCreateProductConfigurationsPanel.attributeName}}"
66+
stepKey="fillFieldForNewAttribute1"/>
67+
<click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute1"/>
68+
<click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue2"/>
69+
<fillField userInput="{{colorProductAttribute2.name}}"
70+
selector="{{AdminCreateProductConfigurationsPanel.attributeName}}"
71+
stepKey="fillFieldForNewAttribute2"/>
72+
<click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute2"/>
73+
<click selector="{{AdminCreateProductConfigurationsPanel.createNewValue}}" stepKey="clickOnCreateNewValue3"/>
74+
<fillField userInput="{{colorProductAttribute3.name}}"
75+
selector="{{AdminCreateProductConfigurationsPanel.attributeName}}"
76+
stepKey="fillFieldForNewAttribute3"/>
77+
<click selector="{{AdminCreateProductConfigurationsPanel.saveAttribute}}" stepKey="clickOnSaveNewAttribute3"/>
78+
<click selector="{{AdminCreateProductConfigurationsPanel.selectAll}}" stepKey="clickOnSelectAll"/>
79+
<click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton2"/>
80+
<click selector="{{AdminCreateProductConfigurationsPanel.applyUniquePricesByAttributeToEachSku}}"
81+
stepKey="clickOnApplyUniquePricesByAttributeToEachSku"/>
82+
<selectOption selector="{{AdminCreateProductConfigurationsPanel.selectAttribute}}"
83+
userInput="{{colorProductAttribute.default_label}}" stepKey="selectAttributes"/>
84+
<fillField selector="{{AdminCreateProductConfigurationsPanel.attribute1}}"
85+
userInput="{{colorProductAttribute1.price}}" stepKey="fillAttributePrice1"/>
86+
<fillField selector="{{AdminCreateProductConfigurationsPanel.attribute2}}"
87+
userInput="{{colorProductAttribute1.price}}" stepKey="fillAttributePrice2"/>
88+
<fillField selector="{{AdminCreateProductConfigurationsPanel.attribute3}}"
89+
userInput="{{colorProductAttribute1.price}}" stepKey="fillAttributePrice3"/>
90+
<click selector="{{AdminCreateProductConfigurationsPanel.applySingleQuantityToEachSkus}}"
91+
stepKey="clickOnApplySingleQuantityToEachSku"/>
92+
<fillField selector="{{AdminCreateProductConfigurationsPanel.quantity}}"
93+
userInput="1" stepKey="enterAttributeQuantity"/>
94+
<click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton3"/>
95+
<click selector="{{AdminCreateProductConfigurationsPanel.next}}" stepKey="clickOnNextButton4"/>
96+
<click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickOnSaveButton2"/>
97+
<click selector="{{AdminChooseAffectedAttributeSetPopup.confirm}}" stepKey="clickOnConfirmInPopup"/>
98+
<seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="seeSaveProductMessage"/>
99+
<seeInTitle userInput="{{product.name}}" stepKey="seeProductNameInTitle"/>
100+
</actionGroup>
101+
</actionGroups>

0 commit comments

Comments
 (0)