Skip to content

Commit 06e5543

Browse files
authored
Merge branch '2.4-develop' into Bundle_product_incorrect_child_item_invoice_qty_when_dynamic_price_is_set_to_false_#30802
2 parents f41c34a + cd857f9 commit 06e5543

File tree

190 files changed

+5384
-680
lines changed

Some content is hidden

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

190 files changed

+5384
-680
lines changed

.github/stale.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ staleLabel: "stale issue"
3939
# Comment to post when marking as stale. Set to `false` to disable
4040
markComment: >
4141
This issue has been automatically marked as stale because it has not had
42-
recent activity. It will be closed after 14 days if no further activity occurs. Thank you
43-
for your contributions.
42+
recent activity. It will be closed after 14 days if no further activity occurs.
43+
Is this issue still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?
44+
Thank you for your contributions!
4445
# Comment to post when removing the stale label.
4546
# unmarkComment: >
4647
# Your comment here.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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="AwsS3AdminImportSimpleAndConfigurableProductsWithAssignedImagesTest" extends="AdminImportSimpleAndConfigurableProductsWithAssignedImagesTest">
12+
<annotations>
13+
<features value="AwsS3"/>
14+
<stories value="Import Products"/>
15+
<title value="S3 - Import Configurable Product With Simple Child Products With Images"/>
16+
<description value="Imports a .csv file containing a configurable product with 3 child simple products that
17+
have images. Verifies that products are imported successfully and that the images are attached to the
18+
products as expected."/>
19+
<severity value="MAJOR"/>
20+
<group value="importExport"/>
21+
<group value="remote_storage_aws_s3"/>
22+
<skip>
23+
<issueId value="MC-39280"/>
24+
</skip>
25+
</annotations>
26+
27+
<before>
28+
<!-- Enable AWS S3 Remote Storage -->
29+
<magentoCLI command="setup:config:set {{RemoteStorageAwsS3ConfigData.enable_options}}" stepKey="enableRemoteStorage"/>
30+
</before>
31+
32+
<after>
33+
<!-- Disable AWS S3 Remote Storage -->
34+
<magentoCLI command="setup:config:set {{RemoteStorageAwsS3ConfigData.disable_options}}" stepKey="disableRemoteStorage"/>
35+
</after>
36+
</test>
37+
</tests>

app/code/Magento/Backend/App/Action/Plugin/Authentication.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@ protected function _redirectIfNeededAfterLogin(\Magento\Framework\App\RequestInt
225225

226226
// Checks, whether secret key is required for admin access or request uri is explicitly set
227227
if ($this->_url->useSecretKey()) {
228-
$requestUri = $this->_url->getUrl('*/*/*', ['_current' => true]);
228+
$requestParts = explode('/', trim($request->getRequestUri(), '/'), 2);
229+
$requestUri = $this->_url->getUrl(array_pop($requestParts));
229230
} elseif ($request) {
230231
$requestUri = $request->getRequestUri();
231232
}

app/code/Magento/Backend/Controller/Adminhtml/Auth/Login.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,10 @@ public function execute()
4949
}
5050

5151
$requestUrl = $this->getRequest()->getUri();
52-
$backendUrl = $this->getUrl('*');
53-
// redirect according to rewrite rule
54-
if ($requestUrl != $backendUrl) {
55-
return $this->getRedirect($backendUrl);
52+
if (!$requestUrl->isValid()) {
53+
return $this->getRedirect($this->getUrl('*'));
5654
}
55+
5756
return $this->resultPageFactory->create();
5857
}
5958

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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="AdminToggleSwitchDynamicPriceOnProductEditPageActionGroup">
12+
<waitForElementVisible selector="{{AdminProductFormBundleSection.dynamicPriceToggle}}" stepKey="waitForToggleDynamicPrice"/>
13+
<checkOption selector="{{AdminProductFormBundleSection.dynamicPriceToggle}}" stepKey="switchDynamicPriceToggle"/>
14+
</actionGroup>
15+
</actionGroups>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
<!-- Test XML Example -->
9+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
10+
<test name="AdminBundleProductPriceValidationErrorDisappearedAfterSwitchToDynamicPriceTest">
11+
<annotations>
12+
<features value="Bundle"/>
13+
<stories value="Create/Edit bundle product in Admin"/>
14+
<title value="Assert error message for price field"/>
15+
<description value="Verify error message for price field is not visible when toggle Dynamic Price is disabled"/>
16+
<severity value="MAJOR"/>
17+
<testCaseId value="MC-40309"/>
18+
<useCaseId value="MC-30152"/>
19+
<group value="bundle"/>
20+
</annotations>
21+
22+
<before>
23+
<actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
24+
</before>
25+
<after>
26+
<actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
27+
</after>
28+
29+
<actionGroup ref="AdminOpenNewProductFormPageActionGroup" stepKey="openNewBundleProductPage">
30+
<argument name="productType" value="bundle"/>
31+
</actionGroup>
32+
<actionGroup ref="AdminToggleSwitchDynamicPriceOnProductEditPageActionGroup" stepKey="disableDynamicPrice"/>
33+
<actionGroup ref="AdminFillProductPriceFieldAndPressEnterOnProductEditPageActionGroup" stepKey="fillProductPriceField">
34+
<argument name="price" value="test"/>
35+
</actionGroup>
36+
<actionGroup ref="AssertAdminValidationErrorAppearedForPriceFieldOnProductEditPageActionGroup" stepKey="assertVisibleError">
37+
<argument name="errorMessage" value="Please enter a number 0 or greater in this field."/>
38+
</actionGroup>
39+
<actionGroup ref="AdminToggleSwitchDynamicPriceOnProductEditPageActionGroup" stepKey="enableDynamicPrice"/>
40+
<actionGroup ref="AssertAdminNoValidationErrorForPriceFieldOnProductEditPageActionGroup" stepKey="assertNotVisibleError"/>
41+
</test>
42+
</tests>

app/code/Magento/Catalog/Model/Indexer/Category/Product/Action/Full.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
use Magento\Catalog\Model\Config;
1313
use Magento\Catalog\Model\Indexer\Category\Product\AbstractAction;
1414
use Magento\Catalog\Model\ResourceModel\Indexer\ActiveTableSwitcher;
15+
use Magento\Catalog\Model\Indexer\Category\Product;
16+
use Magento\Framework\App\DeploymentConfig;
1517
use Magento\Framework\App\ObjectManager;
1618
use Magento\Framework\DB\Adapter\AdapterInterface;
1719
use Magento\Framework\DB\Query\Generator as QueryGenerator;
@@ -63,6 +65,18 @@ class Full extends AbstractAction
6365
*/
6466
private $processManager;
6567

68+
/**
69+
* @var DeploymentConfig|null
70+
*/
71+
private $deploymentConfig;
72+
73+
/**
74+
* Deployment config path
75+
*
76+
* @var string
77+
*/
78+
private const DEPLOYMENT_CONFIG_INDEXER_BATCHES = 'indexer/batch_size/';
79+
6680
/**
6781
* @param ResourceConnection $resource
6882
* @param StoreManagerInterface $storeManager
@@ -73,7 +87,8 @@ class Full extends AbstractAction
7387
* @param MetadataPool|null $metadataPool
7488
* @param int|null $batchRowsCount
7589
* @param ActiveTableSwitcher|null $activeTableSwitcher
76-
* @param ProcessManager $processManager
90+
* @param ProcessManager|null $processManager
91+
* @param DeploymentConfig|null $deploymentConfig
7792
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
7893
*/
7994
public function __construct(
@@ -86,7 +101,8 @@ public function __construct(
86101
MetadataPool $metadataPool = null,
87102
$batchRowsCount = null,
88103
ActiveTableSwitcher $activeTableSwitcher = null,
89-
ProcessManager $processManager = null
104+
ProcessManager $processManager = null,
105+
?DeploymentConfig $deploymentConfig = null
90106
) {
91107
parent::__construct(
92108
$resource,
@@ -107,6 +123,7 @@ public function __construct(
107123
$this->batchRowsCount = $batchRowsCount;
108124
$this->activeTableSwitcher = $activeTableSwitcher ?: $objectManager->get(ActiveTableSwitcher::class);
109125
$this->processManager = $processManager ?: $objectManager->get(ProcessManager::class);
126+
$this->deploymentConfig = $deploymentConfig ?: ObjectManager::getInstance()->get(DeploymentConfig::class);
110127
}
111128

112129
/**
@@ -266,6 +283,11 @@ private function reindexCategoriesBySelect(Select $basicSelect, $whereCondition,
266283
$columns = array_keys(
267284
$this->connection->describeTable($this->tableMaintainer->getMainTmpTable((int)$store->getId()))
268285
);
286+
287+
$this->batchRowsCount = $this->deploymentConfig->get(
288+
self::DEPLOYMENT_CONFIG_INDEXER_BATCHES . Product::INDEXER_ID
289+
) ?? $this->batchRowsCount;
290+
269291
$this->batchSizeManagement->ensureBatchSize($this->connection, $this->batchRowsCount);
270292

271293
$select = $this->connection->select();

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,15 @@ private function getUrlBuilder()
152152
*/
153153
private function isDateWithDateInternal(array $optionValue): bool
154154
{
155-
return array_key_exists('date_internal', $optionValue) && array_key_exists('date', $optionValue);
155+
$hasDate = !empty($optionValue['day'])
156+
&& !empty($optionValue['month'])
157+
&& !empty($optionValue['year']);
158+
159+
$hasTime = !empty($optionValue['hour'])
160+
&& isset($optionValue['minute']);
161+
162+
$hasDateInternal = !empty($optionValue['date_internal']);
163+
164+
return $hasDateInternal && ($hasDate || $hasTime || !empty($optionValue['date']));
156165
}
157166
}

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

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Magento\Framework\Api\ImageContentValidatorInterface;
1818
use Magento\Framework\Api\ImageProcessorInterface;
1919
use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
20+
use Magento\Framework\Api\SearchCriteriaInterface;
2021
use Magento\Framework\DB\Adapter\ConnectionException;
2122
use Magento\Framework\DB\Adapter\DeadlockException;
2223
use Magento\Framework\DB\Adapter\LockWaitException;
@@ -28,6 +29,8 @@
2829
use Magento\Framework\Exception\StateException;
2930
use Magento\Framework\Exception\TemporaryState\CouldNotSaveException as TemporaryCouldNotSaveException;
3031
use Magento\Framework\Exception\ValidatorException;
32+
use Magento\Store\Model\Store;
33+
use Magento\Catalog\Api\Data\EavAttributeInterface;
3134

3235
/**
3336
* @inheritdoc
@@ -514,9 +517,12 @@ public function save(ProductInterface $product, $saveOptions = false)
514517
{
515518
$assignToCategories = false;
516519
$tierPrices = $product->getData('tier_price');
520+
$productDataToChange = $product->getData();
517521

518522
try {
519-
$existingProduct = $product->getId() ? $this->getById($product->getId()) : $this->get($product->getSku());
523+
$existingProduct = $product->getId() ?
524+
$this->getById($product->getId()) :
525+
$this->get($product->getSku());
520526

521527
$product->setData(
522528
$this->resourceModel->getLinkField(),
@@ -548,7 +554,6 @@ public function save(ProductInterface $product, $saveOptions = false)
548554
$productDataArray['store_id'] = (int) $this->storeManager->getStore()->getId();
549555
}
550556
$product = $this->initializeProductData($productDataArray, empty($existingProduct));
551-
552557
$this->processLinks($product, $productLinks);
553558
if (isset($productDataArray['media_gallery'])) {
554559
$this->processMediaGallery($product, $productDataArray['media_gallery']['images']);
@@ -569,6 +574,42 @@ public function save(ProductInterface $product, $saveOptions = false)
569574
$product->setData('tier_price', $tierPrices);
570575
}
571576

577+
try {
578+
$stores = $product->getStoreIds();
579+
$websites = $product->getWebsiteIds();
580+
} catch (NoSuchEntityException $exception) {
581+
$stores = null;
582+
$websites = null;
583+
}
584+
585+
if (!empty($existingProduct) && is_array($stores) && is_array($websites)) {
586+
$hasDataChanged = false;
587+
$productAttributes = $product->getAttributes();
588+
if ($productAttributes !== null
589+
&& $product->getStoreId() !== Store::DEFAULT_STORE_ID
590+
&& (count($stores) > 1 || count($websites) === 1)
591+
) {
592+
foreach ($productAttributes as $attribute) {
593+
$attributeCode = $attribute->getAttributeCode();
594+
$value = $product->getData($attributeCode);
595+
if ($existingProduct->getData($attributeCode) === $value
596+
&& $attribute->getScope() !== EavAttributeInterface::SCOPE_GLOBAL_TEXT
597+
&& !is_array($value)
598+
&& $attribute->getData('frontend_input') !== 'media_image'
599+
&& !$attribute->isStatic()
600+
&& !array_key_exists($attributeCode, $productDataToChange)
601+
&& $value !== null
602+
) {
603+
$product->setData($attributeCode);
604+
$hasDataChanged = true;
605+
}
606+
}
607+
if ($hasDataChanged) {
608+
$product->setData('_edit_mode', true);
609+
}
610+
}
611+
}
612+
572613
$this->saveProduct($product);
573614
if ($assignToCategories === true && $product->getCategoryIds()) {
574615
$this->linkManagement->assignProductToCategories(
@@ -619,7 +660,7 @@ public function deleteById($sku)
619660
/**
620661
* @inheritdoc
621662
*/
622-
public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria)
663+
public function getList(SearchCriteriaInterface $searchCriteria)
623664
{
624665
/** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */
625666
$collection = $this->collectionFactory->create();
@@ -628,6 +669,7 @@ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCr
628669
$collection->addAttributeToSelect('*');
629670
$collection->joinAttribute('status', 'catalog_product/status', 'entity_id', null, 'inner');
630671
$collection->joinAttribute('visibility', 'catalog_product/visibility', 'entity_id', null, 'inner');
672+
$this->joinPositionField($collection, $searchCriteria);
631673

632674
$this->collectionProcessor->process($searchCriteria, $collection);
633675

@@ -856,4 +898,36 @@ private function saveProduct($product): void
856898
);
857899
}
858900
}
901+
902+
/**
903+
* Join category position field to make sorting by position possible.
904+
*
905+
* @param Collection $collection
906+
* @param SearchCriteriaInterface $searchCriteria
907+
* @return void
908+
*/
909+
private function joinPositionField(
910+
Collection $collection,
911+
SearchCriteriaInterface $searchCriteria
912+
): void {
913+
$categoryIds = [[]];
914+
foreach ($searchCriteria->getFilterGroups() as $filterGroup) {
915+
foreach ($filterGroup->getFilters() as $filter) {
916+
if ($filter->getField() === 'category_id') {
917+
$categoryIds[] = explode(',', $filter->getValue());
918+
}
919+
}
920+
}
921+
$categoryIds = array_unique(array_merge(...$categoryIds));
922+
if (count($categoryIds) === 1) {
923+
$collection->joinField(
924+
'position',
925+
'catalog_category_product',
926+
'position',
927+
'product_id=entity_id',
928+
['category_id' => current($categoryIds)],
929+
'left'
930+
);
931+
}
932+
}
859933
}

app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,12 @@ public function addWebsiteFilter($websites = null)
916916
}
917917

918918
$this->_productLimitationFilters['website_ids'] = $websites;
919-
$this->_applyProductLimitations();
919+
920+
if ($this->getStoreId() == Store::DEFAULT_STORE_ID) {
921+
$this->_productLimitationJoinWebsite();
922+
} else {
923+
$this->_applyProductLimitations();
924+
}
920925

921926
return $this;
922927
}

0 commit comments

Comments
 (0)