Skip to content

Commit 8e1df76

Browse files
authored
Merge branch 'magento-commerce:2.4-develop' into ACPT-1666
2 parents a3ef8a2 + 2a55516 commit 8e1df76

File tree

47 files changed

+964
-217
lines changed

Some content is hidden

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

47 files changed

+964
-217
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class Category extends \Magento\Catalog\Model\AbstractModel implements
109109
*
110110
* @var \Magento\UrlRewrite\Model\UrlRewrite
111111
* @deprecated 102.0.0
112+
* @see \Magento\UrlRewrite\Model\UrlFinderInterface
112113
*/
113114
protected $_urlRewrite;
114115

@@ -315,6 +316,7 @@ protected function getCustomAttributesCodes()
315316
* @throws \Magento\Framework\Exception\LocalizedException
316317
* @return \Magento\Catalog\Model\ResourceModel\Category
317318
* @deprecated 102.0.6 because resource models should be used directly
319+
* @see \Magento\Catalog\Model\ResourceModel\Category
318320
* phpcs:disable Generic.CodeAnalysis.UselessOverridingMethod
319321
* @since 102.0.6
320322
*/
@@ -615,6 +617,7 @@ public function getUrl()
615617
UrlRewrite::ENTITY_ID => $this->getId(),
616618
UrlRewrite::ENTITY_TYPE => CategoryUrlRewriteGenerator::ENTITY_TYPE,
617619
UrlRewrite::STORE_ID => $this->getStoreId(),
620+
UrlRewrite::REDIRECT_TYPE => 0
618621
]
619622
);
620623
if ($rewrite) {

app/code/Magento/Catalog/Model/ProductRepository.php

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,6 @@ public function save(ProductInterface $product, $saveOptions = false)
526526
$assignToCategories = false;
527527
$tierPrices = $product->getData('tier_price');
528528
$productDataToChange = $product->getData();
529-
530529
try {
531530
$existingProduct = $product->getId() ?
532531
$this->getById($product->getId()) :
@@ -597,14 +596,16 @@ public function save(ProductInterface $product, $saveOptions = false)
597596
&& $product->getStoreId() !== Store::DEFAULT_STORE_ID
598597
&& (count($stores) > 1 || count($websites) === 1)
599598
) {
599+
$imageRoles = ['image', 'small_image', 'thumbnail'];
600600
foreach ($productAttributes as $attribute) {
601601
$attributeCode = $attribute->getAttributeCode();
602602
$value = $product->getData($attributeCode);
603-
if ($existingProduct->getData($attributeCode) === $value
603+
if (!in_array($attributeCode, $imageRoles)
604+
&& $existingProduct->getData($attributeCode) === $value
605+
&& $existingProduct->getOrigData($attributeCode) === $value
604606
&& $attribute->getScope() !== EavAttributeInterface::SCOPE_GLOBAL_TEXT
605607
&& !is_array($value)
606608
&& !$attribute->isStatic()
607-
&& !array_key_exists($attributeCode, $productDataToChange)
608609
&& $value !== null
609610
&& !$this->scopeOverriddenValue->containsValue(
610611
ProductInterface::class,
@@ -618,6 +619,21 @@ public function save(ProductInterface $product, $saveOptions = false)
618619
$attributeCode === ProductAttributeInterface::CODE_SEO_FIELD_URL_KEY ? false : null
619620
);
620621
$hasDataChanged = true;
622+
} elseif (in_array($attributeCode, $imageRoles)
623+
&& $existingProduct->getData($attributeCode) === $value
624+
&& !array_key_exists($attributeCode, $productDataToChange)
625+
&& $attribute->getScope() !== EavAttributeInterface::SCOPE_GLOBAL_TEXT
626+
&& $value !== null
627+
&& !$this->scopeOverriddenValue->containsValue(
628+
ProductInterface::class,
629+
$product,
630+
$attributeCode,
631+
$product->getStoreId()
632+
)
633+
634+
) {
635+
$product->setData($attributeCode, null);
636+
$hasDataChanged = true;
621637
}
622638
}
623639
if ($hasDataChanged) {

app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,6 @@ public function getQuery(array $dimensions, string $productType, array $entityId
230230
$select->where('cgw.website_id IS NULL');
231231

232232
if ($entityIds !== null) {
233-
$select->where(sprintf('e.entity_id BETWEEN %s AND %s', min($entityIds), max($entityIds)));
234233
$select->where('e.entity_id IN(?)', $entityIds, \Zend_Db::INT_TYPE);
235234
}
236235

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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="ProductViewPageCustomOptionValidationErrorMessageTest">
12+
<annotations>
13+
<features value="Catalog"/>
14+
<stories value="Product view page"/>
15+
<title value="[Magento Cloud] Bug of product custom option validation in product view page"/>
16+
<description value="Check custom option validation error message is displayed in product view page"/>
17+
<severity value="AVERAGE"/>
18+
<testCaseId value="AC-9978"/>
19+
<useCaseId value="ACP2E-2404"/>
20+
<group value="Catalog"/>
21+
</annotations>
22+
<before>
23+
<actionGroup ref="AdminLoginActionGroup" stepKey="LoginAsAdmin" />
24+
<createData entity="SimpleProduct2" stepKey="createProduct"/>
25+
<!-- open product edit page -->
26+
<actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToProductEditPage">
27+
<argument name="productId" value="$$createProduct.id$$"/>
28+
</actionGroup>
29+
<!-- Create a custom option(radio button) with 2 values -->
30+
<click stepKey="openCustomizableOptions" selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}"/>
31+
<waitForPageLoad stepKey="waitForCustomOptionsOpen"/>
32+
<actionGroup ref="CreateCustomRadioOptionsActionGroup" stepKey="createCustomOption1">
33+
<argument name="customOptionName" value="ProductOptionRadiobutton.title"/>
34+
<argument name="productOption" value="ProductOptionField"/>
35+
<argument name="productOption2" value="ProductOptionField2"/>
36+
</actionGroup>
37+
<!-- Save the product -->
38+
<actionGroup ref="AdminProductFormSaveActionGroup" stepKey="saveProduct"/>
39+
<seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccess"/>
40+
<!-- indexer reindex -->
41+
<actionGroup ref="CliIndexerReindexActionGroup" stepKey="reindex">
42+
<argument name="indices" value=""/>
43+
</actionGroup>
44+
</before>
45+
<after>
46+
<deleteData createDataKey="createProduct" stepKey="deleteProduct"/>
47+
<actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
48+
</after>
49+
<!-- Navigate to Product Page on StoreFront -->
50+
<actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openStorefrontProductPage">
51+
<argument name="productUrl" value="$$createProduct.custom_attributes[url_key]$$"/>
52+
</actionGroup>
53+
<!-- Add Product to Cart from product detail page -->
54+
<click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/>
55+
<!-- see custom option validation message -->
56+
<see userInput="This is a required field." stepKey="seeRequiredField"/>
57+
</test>
58+
</tests>

app/code/Magento/Catalog/view/frontend/web/product/view/validation.js

Lines changed: 59 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -12,78 +12,78 @@ define([
1212

1313
$.widget('mage.validation', $.mage.validation, {
1414
options: {
15-
radioCheckboxClosest: 'ul, ol',
15+
radioCheckboxClosest: 'ul, ol'
16+
},
1617

17-
/**
18-
* @param {*} error
19-
* @param {HTMLElement} element
20-
*/
21-
errorPlacement: function (error, element) {
22-
var messageBox,
23-
dataValidate;
18+
/**
19+
* @param {*} error
20+
* @param {HTMLElement} element
21+
*/
22+
errorPlacement: function (error, element) {
23+
var messageBox,
24+
dataValidate;
2425

25-
if ($(element).hasClass('datetime-picker')) {
26-
element = $(element).parent();
26+
if ($(element).hasClass('datetime-picker')) {
27+
element = $(element).parent();
2728

28-
if (element.parent().find('.mage-error').length) {
29-
return;
30-
}
29+
if (element.parent().find('.mage-error').length) {
30+
return;
3131
}
32+
}
3233

33-
if (element.attr('data-errors-message-box')) {
34-
messageBox = $(element.attr('data-errors-message-box'));
35-
messageBox.html(error);
34+
if (element.attr('data-errors-message-box')) {
35+
messageBox = $(element.attr('data-errors-message-box'));
36+
messageBox.html(error);
3637

37-
return;
38-
}
38+
return;
39+
}
3940

40-
dataValidate = element.attr('data-validate');
41+
dataValidate = element.attr('data-validate');
4142

42-
if (dataValidate && dataValidate.indexOf('validate-one-checkbox-required-by-name') > 0) {
43-
error.appendTo('#links-advice-container');
44-
} else if (element.is(':radio, :checkbox')) {
45-
element.closest(this.radioCheckboxClosest).after(error);
46-
} else {
47-
element.after(error);
48-
}
49-
},
43+
if (dataValidate && dataValidate.indexOf('validate-one-checkbox-required-by-name') > 0) {
44+
error.appendTo('#links-advice-container');
45+
} else if (element.is(':radio, :checkbox')) {
46+
element.closest(this.radioCheckboxClosest).after(error);
47+
} else {
48+
element.after(error);
49+
}
50+
},
5051

51-
/**
52-
* @param {HTMLElement} element
53-
* @param {String} errorClass
54-
*/
55-
highlight: function (element, errorClass) {
56-
var dataValidate = $(element).attr('data-validate');
52+
/**
53+
* @param {HTMLElement} element
54+
* @param {String} errorClass
55+
*/
56+
highlight: function (element, errorClass) {
57+
var dataValidate = $(element).attr('data-validate');
5758

58-
if (dataValidate && dataValidate.indexOf('validate-required-datetime') > 0) {
59-
$(element).parent().find('.datetime-picker').each(function () {
60-
$(this).removeClass(errorClass);
59+
if (dataValidate && dataValidate.indexOf('validate-required-datetime') > 0) {
60+
$(element).parent().find('.datetime-picker').each(function () {
61+
$(this).removeClass(errorClass);
6162

62-
if ($(this).val().length === 0) {
63-
$(this).addClass(errorClass);
64-
}
65-
});
66-
} else if ($(element).is(':radio, :checkbox')) {
67-
$(element).closest(this.radioCheckboxClosest).addClass(errorClass);
68-
} else {
69-
$(element).addClass(errorClass);
70-
}
71-
},
63+
if ($(this).val().length === 0) {
64+
$(this).addClass(errorClass);
65+
}
66+
});
67+
} else if ($(element).is(':radio, :checkbox')) {
68+
$(element).closest(this.radioCheckboxClosest).addClass(errorClass);
69+
} else {
70+
$(element).addClass(errorClass);
71+
}
72+
},
7273

73-
/**
74-
* @param {HTMLElement} element
75-
* @param {String} errorClass
76-
*/
77-
unhighlight: function (element, errorClass) {
78-
var dataValidate = $(element).attr('data-validate');
74+
/**
75+
* @param {HTMLElement} element
76+
* @param {String} errorClass
77+
*/
78+
unhighlight: function (element, errorClass) {
79+
var dataValidate = $(element).attr('data-validate');
7980

80-
if (dataValidate && dataValidate.indexOf('validate-required-datetime') > 0) {
81-
$(element).parent().find('.datetime-picker').removeClass(errorClass);
82-
} else if ($(element).is(':radio, :checkbox')) {
83-
$(element).closest(this.radioCheckboxClosest).removeClass(errorClass);
84-
} else {
85-
$(element).removeClass(errorClass);
86-
}
81+
if (dataValidate && dataValidate.indexOf('validate-required-datetime') > 0) {
82+
$(element).parent().find('.datetime-picker').removeClass(errorClass);
83+
} else if ($(element).is(':radio, :checkbox')) {
84+
$(element).closest(this.radioCheckboxClosest).removeClass(errorClass);
85+
} else {
86+
$(element).removeClass(errorClass);
8787
}
8888
}
8989
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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="CheckoutSelectCreditCardPaymentActionGroup">
12+
<annotations>
13+
<description>Selects the 'Credit card' Payment Method on the Storefront Checkout page.</description>
14+
</annotations>
15+
16+
<waitForPageLoad stepKey="waitForLoadingMask"/>
17+
<waitForPageLoad stepKey="waitForPageLoad"/>
18+
<click selector="{{StorefrontCheckoutPaymentMethodSection.checkCreditCard}}" stepKey="selectCreditCardPaymentMethod"/>
19+
<waitForPageLoad stepKey="waitForLoadingMaskAfterPaymentMethodSelection"/>
20+
</actionGroup>
21+
</actionGroups>

app/code/Magento/Checkout/Test/Mftf/Section/StorefrontCheckoutPaymentMethodSection.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<section name="StorefrontCheckoutPaymentMethodSection">
1212
<element name="billingAddress" type="text" selector=".checkout-billing-address"/>
1313
<element name="checkPaymentMethodByName" type="radio" selector="//div[@id='checkout-payment-method-load']//div[@class='payment-method']//label//span[contains(., '{{methodName}}')]/../..//input" parameterized="true"/>
14+
<element name="checkCreditCard" type="radio" selector="//div[@id='checkout-payment-method-load']//div[@class='payment-method payment-method-braintree']//label//span[contains(., 'Credit Card')]/../..//input"/>
1415
<element name="billingAddressSameAsShipping" type="checkbox" selector=".payment-method._active [name='billing-address-same-as-shipping']"/>
1516
<element name="billingAddressSameAsShippingShared" type="checkbox" selector="#billing-address-same-as-shipping-shared"/>
1617
<element name="paymentOnAccount" type="radio" selector="#companycredit" deprecated="Use StorefrontCheckoutPaymentSection.paymentOnAccount B2B repository"/>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
/**
3+
* Copyright 2023 Adobe
4+
* All Rights Reserved.
5+
*
6+
* NOTICE: All information contained herein is, and remains
7+
* the property of Adobe and its suppliers, if any. The intellectual
8+
* and technical concepts contained herein are proprietary to Adobe
9+
* and its suppliers and are protected by all applicable intellectual
10+
* property laws, including trade secret and copyright laws.
11+
* Dissemination of this information or reproduction of this material
12+
* is strictly forbidden unless prior written permission is obtained from
13+
* Adobe.
14+
*/
15+
declare(strict_types=1);
16+
17+
namespace Magento\Customer\Model\Cache;
18+
19+
class GroupExcludedWebsiteCache
20+
{
21+
/**
22+
* @var array
23+
*/
24+
private array $customerGroupExcludedWebsite = [];
25+
26+
/**
27+
* Adds entry to GroupExcludedWebsite cache
28+
*
29+
* @param int $customerGroupId
30+
* @param array $value
31+
*/
32+
public function addToCache(int $customerGroupId, array $value)
33+
{
34+
$this->customerGroupExcludedWebsite[$customerGroupId] = $value;
35+
}
36+
37+
/**
38+
* Gets entry from GroupExcludedWebsite cache
39+
*
40+
* @param int $customerGroupId
41+
* @return array
42+
*/
43+
public function getFromCache(int $customerGroupId): array
44+
{
45+
return $this->customerGroupExcludedWebsite[$customerGroupId] ?? [];
46+
}
47+
48+
/**
49+
* Checks presence of cached customer group in GroupExcludedWebsite cache
50+
*
51+
* @param int $customerGroupId
52+
* @return bool
53+
*/
54+
public function isCached(int $customerGroupId): bool
55+
{
56+
return isset($this->customerGroupExcludedWebsite[$customerGroupId]);
57+
}
58+
59+
/**
60+
* Cleans the cache
61+
*/
62+
public function invalidate()
63+
{
64+
$this->customerGroupExcludedWebsite = [];
65+
}
66+
}

app/code/Magento/Customer/Model/Plugin/SaveCustomerGroupExcludedWebsite.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,5 +145,4 @@ private function isValueChanged(array $currentValues, array $newValues): bool
145145
return !($currentValues === array_intersect($currentValues, $newValues)
146146
&& $newValues === array_intersect($newValues, $currentValues));
147147
}
148-
149148
}

0 commit comments

Comments
 (0)