Skip to content

Commit 2de1643

Browse files
committed
Merge branch '2.4-develop' of https://github.com/magento-gl/magento2ce into ACQE-6372
2 parents 5390cca + fcc6f0a commit 2de1643

File tree

60 files changed

+3551
-166
lines changed

Some content is hidden

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

60 files changed

+3551
-166
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
/************************************************************************
3+
*
4+
* Copyright 2024 Adobe
5+
* All Rights Reserved.
6+
*
7+
* NOTICE: All information contained herein is, and remains
8+
* the property of Adobe and its suppliers, if any. The intellectual
9+
* and technical concepts contained herein are proprietary to Adobe
10+
* and its suppliers and are protected by all applicable intellectual
11+
* property laws, including trade secret and copyright laws.
12+
* Dissemination of this information or reproduction of this material
13+
* is strictly forbidden unless prior written permission is obtained
14+
* from Adobe.
15+
* ************************************************************************
16+
*/
17+
declare(strict_types=1);
18+
19+
namespace Magento\Bundle\Model\Product;
20+
21+
use Magento\Catalog\Model\Product;
22+
use Magento\Framework\Serialize\Serializer\Json;
23+
24+
/**
25+
* Get original price for bundle products
26+
*/
27+
class OriginalPrice
28+
{
29+
/**
30+
* @param Json $serializer
31+
*/
32+
public function __construct(private readonly Json $serializer)
33+
{
34+
}
35+
36+
/**
37+
* Get Original Total price for Bundle items
38+
*
39+
* @param Product $product
40+
* @return float
41+
*/
42+
public function getTotalBundleItemsOriginalPrice(Product $product): float
43+
{
44+
$price = 0.0;
45+
46+
if (!$product->hasCustomOptions()) {
47+
return $price;
48+
}
49+
50+
$selectionIds = $this->getBundleSelectionIds($product);
51+
52+
if (empty($selectionIds)) {
53+
return $price;
54+
}
55+
56+
$selections = $product->getTypeInstance()->getSelectionsByIds($selectionIds, $product);
57+
foreach ($selections->getItems() as $selection) {
58+
if (!$selection->isSalable()) {
59+
continue;
60+
}
61+
62+
$selectionQty = $product->getCustomOption('selection_qty_' . $selection->getSelectionId());
63+
if ($selectionQty) {
64+
$price += $this->getSelectionOriginalTotalPrice(
65+
$product,
66+
$selection,
67+
(float) $selectionQty->getValue()
68+
);
69+
}
70+
}
71+
72+
return $price;
73+
}
74+
75+
/**
76+
* Calculate total original price of selection
77+
*
78+
* @param Product $bundleProduct
79+
* @param Product $selectionProduct
80+
* @param float $selectionQty
81+
*
82+
* @return float
83+
*/
84+
private function getSelectionOriginalTotalPrice(
85+
Product $bundleProduct,
86+
Product $selectionProduct,
87+
float $selectionQty
88+
): float {
89+
$price = $this->getSelectionOriginalPrice($bundleProduct, $selectionProduct);
90+
91+
return $price * $selectionQty;
92+
}
93+
94+
/**
95+
* Calculate the original price of selection
96+
*
97+
* @param Product $bundleProduct
98+
* @param Product $selectionProduct
99+
*
100+
* @return float
101+
*/
102+
public function getSelectionOriginalPrice(Product $bundleProduct, Product $selectionProduct): float
103+
{
104+
if ($bundleProduct->getPriceType() == Price::PRICE_TYPE_DYNAMIC) {
105+
return (float) $selectionProduct->getPrice();
106+
}
107+
if ($selectionProduct->getSelectionPriceType()) {
108+
// percent
109+
return $bundleProduct->getPrice() * ($selectionProduct->getSelectionPriceValue() / 100);
110+
}
111+
112+
// fixed
113+
return (float) $selectionProduct->getSelectionPriceValue();
114+
}
115+
116+
/**
117+
* Retrieve array of bundle selection IDs
118+
*
119+
* @param Product $product
120+
* @return array
121+
*/
122+
private function getBundleSelectionIds(Product $product): array
123+
{
124+
$customOption = $product->getCustomOption('bundle_selection_ids');
125+
if ($customOption) {
126+
$selectionIds = $this->serializer->unserialize($customOption->getValue());
127+
if (is_array($selectionIds)) {
128+
return $selectionIds;
129+
}
130+
}
131+
return [];
132+
}
133+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
/************************************************************************
3+
*
4+
* Copyright 2024 Adobe
5+
* All Rights Reserved.
6+
*
7+
* NOTICE: All information contained herein is, and remains
8+
* the property of Adobe and its suppliers, if any. The intellectual
9+
* and technical concepts contained herein are proprietary to Adobe
10+
* and its suppliers and are protected by all applicable intellectual
11+
* property laws, including trade secret and copyright laws.
12+
* Dissemination of this information or reproduction of this material
13+
* is strictly forbidden unless prior written permission is obtained
14+
* from Adobe.
15+
* ************************************************************************
16+
*/
17+
declare(strict_types=1);
18+
19+
namespace Magento\Bundle\Plugin\Quote;
20+
21+
use Magento\Bundle\Model\Product\OriginalPrice;
22+
use Magento\Bundle\Model\Product\Type;
23+
use Magento\Quote\Model\Quote;
24+
use Magento\Quote\Model\Quote\Address\Total\Subtotal;
25+
use Magento\Quote\Api\Data\ShippingAssignmentInterface;
26+
use Magento\Quote\Model\Quote\Address\Total;
27+
28+
/**
29+
* Update bundle base original price
30+
*/
31+
class UpdateBundleQuoteItemBaseOriginalPrice
32+
{
33+
/**
34+
* @param OriginalPrice $price
35+
*/
36+
public function __construct(
37+
private readonly OriginalPrice $price
38+
) {
39+
}
40+
41+
/**
42+
* Update bundle base original price
43+
*
44+
* @param Subtotal $subject
45+
* @param Subtotal $result
46+
* @param Quote $quote
47+
* @param ShippingAssignmentInterface $shippingAssignment
48+
* @param Total $total
49+
*
50+
* @return Subtotal
51+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
52+
*/
53+
public function afterCollect(
54+
Subtotal $subject,
55+
Subtotal $result,
56+
Quote $quote,
57+
ShippingAssignmentInterface $shippingAssignment,
58+
Total $total
59+
): Subtotal {
60+
foreach ($quote->getAllVisibleItems() as $quoteItem) {
61+
if ($quoteItem->getProductType() === Type::TYPE_CODE) {
62+
$price = $quoteItem->getProduct()->getPrice();
63+
$price += $this->price->getTotalBundleItemsOriginalPrice($quoteItem->getProduct());
64+
$quoteItem->setBaseOriginalPrice($price);
65+
}
66+
}
67+
return $result;
68+
}
69+
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,4 +284,9 @@
284284
</argument>
285285
</arguments>
286286
</type>
287+
<type name="Magento\Quote\Model\Quote\Address\Total\Subtotal">
288+
<plugin name="update_bundle_quote_item_base_original_price"
289+
type="Magento\Bundle\Plugin\Quote\UpdateBundleQuoteItemBaseOriginalPrice"
290+
sortOrder="10"/>
291+
</type>
287292
</config>

app/code/Magento/BundleGraphQl/Model/Resolver/BundlePriceDetails.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
2828

2929
$price = $product->getPrice();
3030
$finalPrice = $product->getFinalPrice();
31-
$discountPercentage = 100 - (($finalPrice * 100) / $price);
31+
$discountPercentage = ($price) ? (100 - (($finalPrice * 100) / $price)) : 0;
3232
return [
3333
'main_price' => $price,
3434
'main_final_price' => $finalPrice,

app/code/Magento/Catalog/Model/Category/FileInfo.php

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
use Magento\Framework\Filesystem\Directory\ReadInterface;
1515
use Magento\Framework\Exception\NoSuchEntityException;
1616
use Magento\Framework\Filesystem\ExtendedDriverInterface;
17+
use Magento\Framework\ObjectManager\ResetAfterRequestInterface;
1718
use Magento\Store\Model\StoreManagerInterface;
1819

1920
/**
2021
* Class FileInfo
2122
*
2223
* Provides information about requested file
2324
*/
24-
class FileInfo
25+
class FileInfo implements ResetAfterRequestInterface
2526
{
2627
/**
2728
* Path in /pub/media directory
@@ -31,12 +32,12 @@ class FileInfo
3132
/**
3233
* @var Filesystem
3334
*/
34-
private $filesystem;
35+
private readonly Filesystem $filesystem;
3536

3637
/**
3738
* @var Mime
3839
*/
39-
private $mime;
40+
private readonly Mime $mime;
4041

4142
/**
4243
* @var WriteInterface
@@ -56,7 +57,7 @@ class FileInfo
5657
/**
5758
* @var \Magento\Store\Model\StoreManagerInterface
5859
*/
59-
private $storeManager;
60+
private readonly StoreManagerInterface $storeManager;
6061

6162
/**
6263
* @param Filesystem $filesystem
@@ -73,6 +74,16 @@ public function __construct(
7374
$this->storeManager = $storeManager;
7475
}
7576

77+
/**
78+
* @inheritDoc
79+
*/
80+
public function _resetState(): void
81+
{
82+
$this->mediaDirectory = null;
83+
$this->baseDirectory = null;
84+
$this->pubDirectory = null;
85+
}
86+
7687
/**
7788
* Get WriteInterface instance
7889
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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="StorefrontAssertProductPriceAndPriceLabelIfPresentOnCategoryPageActionGroup">
12+
<annotations>
13+
<description>Validate that the price and price label if present on category page.</description>
14+
</annotations>
15+
<arguments>
16+
<argument name="productName" type="string"/>
17+
<argument name="price" type="string"/>
18+
<argument name="priceLabel" type="string"/>
19+
</arguments>
20+
<waitForElementVisible selector="{{StorefrontCategoryMainSection.productNameWithPriceOrLabel(productName,priceLabel)}}" stepKey="assertProductPriceLabel"/>
21+
<waitForElementVisible selector="{{StorefrontCategoryMainSection.productNameWithPriceOrLabel(productName,price)}}" stepKey="assertProductPrice"/>
22+
</actionGroup>
23+
</actionGroups>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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="StorefrontAssertProductPriceAndPriceLabelIsNotPresentOnCategoryPageActionGroup">
12+
<annotations>
13+
<description>Validate that the price and price label is not present on category page.</description>
14+
</annotations>
15+
<arguments>
16+
<argument name="productName" type="string"/>
17+
<argument name="price" type="string"/>
18+
<argument name="priceLabel" type="string"/>
19+
</arguments>
20+
<dontSeeElement selector="{{StorefrontCategoryMainSection.productNameWithPriceOrLabel(productName,priceLabel)}}" stepKey="dontSeePriceLabel"/>
21+
<waitForElementVisible selector="{{StorefrontCategoryMainSection.productNameWithPriceOrLabel(productName,price)}}" stepKey="assertProductPrice"/>
22+
</actionGroup>
23+
</actionGroups>

app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
<element name="outOfStockProductCategoryPage" type="text" selector="//div[@class='stock unavailable']//span[text()='Out of stock']"/>
4747
<element name="ListedProductAttributes" type="block" selector="//div[@aria-label='{{vs_attribute}}']//div[@aria-label='{{attribute_name}}']" parameterized="true"/>
4848
<element name="quickOrderLink" type="text" selector="//div[@class='panel header']//a[text()='Quick Order']" />
49+
<element name="productNameWithPriceOrLabel" type="text" selector="//div[@class='product details product-item-details']//a[contains(text(),'{{ProductName}}')]//parent::strong/following-sibling::div//span[contains(text(),'{{PriceLabelOrPrice}}')]" parameterized="true"/>
4950
<element name="sortByDropdownContent" type="select" selector="//select[@id='sorter']//option[contains(text(),'{{arg}}')]" parameterized="true"/>
5051
<element name="productInOrderDisplay" type="text" selector="//li[@class='item product product-item'][{{index}}]//a[@class='product-item-link' and contains(text(),'{{product_name}}')]" parameterized="true"/>
5152
</section>

app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductActionSection.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
1010
<section name="StorefrontProductActionSection">
1111
<element name="quantity" type="input" selector="#qty"/>
12-
<element name="addToCart" type="button" selector="#product-addtocart-button" timeout="60"/>
12+
<element name="addToCart" type="button" selector=".box-tocart .fieldset .actions #product-addtocart-button" timeout="60"/>
1313
<element name="addToCartDisabled" type="button" selector="#product-addtocart-button[disabled]" timeout="60"/>
1414
<element name="addToCartEnabledWithTranslation" type="button" selector="button#product-addtocart-button[data-translate]:enabled" timeout="60"/>
1515
<element name="addToCartButtonTitleIsAdding" type="text" selector="//button/span[text()='Adding...']"/>

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
<magentoCLI command="config:set checkout/options/enable_guest_checkout_login 1" stepKey="EnablingGuestCheckoutLogin"/>
2323
</before>
2424
<after>
25+
<actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="goToProductList"/>
26+
<actionGroup ref="AdminClearGridFiltersActionGroup" stepKey="resetProductFilters"/>
2527
<actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/>
2628
<magentoCLI command="config:set checkout/options/enable_guest_checkout_login 0" stepKey="DisablingGuestCheckoutLogin"/>
2729
</after>

0 commit comments

Comments
 (0)