Skip to content

Commit c6b47db

Browse files
committed
Merge branch '2.4-develop' of github.com:magento-amigos/magento2ce into 2.4-develop-prs-patch-2
2 parents b1cc54e + c6aeb6a commit c6b47db

File tree

55 files changed

+1525
-429
lines changed

Some content is hidden

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

55 files changed

+1525
-429
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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\Bundle\Test\Fixture;
9+
10+
use Magento\Bundle\Model\Product\Type;
11+
use Magento\Catalog\Api\ProductRepositoryInterface;
12+
use Magento\Framework\DataObject;
13+
use Magento\Framework\DataObjectFactory;
14+
use Magento\Quote\Api\CartRepositoryInterface;
15+
16+
class AddProductToCart extends \Magento\Quote\Test\Fixture\AddProductToCart
17+
{
18+
/**
19+
* @var ProductRepositoryInterface
20+
*/
21+
private ProductRepositoryInterface $productRepository;
22+
23+
/**
24+
* @var Type
25+
*/
26+
private Type $productType;
27+
28+
/**
29+
* @param CartRepositoryInterface $cartRepository
30+
* @param ProductRepositoryInterface $productRepository
31+
* @param DataObjectFactory $dataObjectFactory
32+
* @param Type $productType
33+
*/
34+
public function __construct(
35+
CartRepositoryInterface $cartRepository,
36+
ProductRepositoryInterface $productRepository,
37+
DataObjectFactory $dataObjectFactory,
38+
Type $productType
39+
) {
40+
parent::__construct($cartRepository, $productRepository, $dataObjectFactory);
41+
$this->productRepository = $productRepository;
42+
$this->productType = $productType;
43+
}
44+
45+
/**
46+
* {@inheritdoc}
47+
* @param array $data Parameters
48+
* $data['selections'] can be supplied in following formats:
49+
* - [["$product1.id$"], ["$product2.id$"]]
50+
* - [[{"product_id":"$product1.id$","qty":1}], [{"product_id":"$product2.id$","qty":1}]]
51+
* - To skip an option, pass empty array [["$product1.id$"], [], ["$product2.id$"]]
52+
* <pre>
53+
* $data = [
54+
* 'cart_id' => (int) Cart ID. Required.
55+
* 'product_id' => (int) Product ID. Required.
56+
* 'selections' => (array) array of options selections. Required.
57+
* 'qty' => (int) Quantity. Optional. Default: 1.
58+
* ]
59+
* </pre>
60+
*/
61+
public function apply(array $data = []): ?DataObject
62+
{
63+
$bundleProduct = $this->productRepository->getById($data['product_id']);
64+
$buyRequest = [
65+
'bundle_option' => [],
66+
'bundle_option_qty' => [],
67+
'qty' => $data['qty'] ?? 1,
68+
];
69+
$options = $this->productType->getOptionsCollection($bundleProduct);
70+
$selections = $this->productType->getSelectionsCollection([], $bundleProduct);
71+
$options->appendSelections($selections, true);
72+
$optionsList = array_values($options->getItems());
73+
foreach ($data['selections'] as $index => $selections) {
74+
if (!empty($selections)) {
75+
$option = $optionsList[$index];
76+
foreach ($selections as $item) {
77+
if (is_array($item)) {
78+
$productId = (int)$item['product_id'];
79+
$qty = $item['qty'] ?? 1;
80+
} else {
81+
$productId = (int)$item;
82+
$qty = 1;
83+
}
84+
foreach ($option->getSelections() as $selection) {
85+
if (((int)$selection->getProductId()) === $productId) {
86+
$buyRequest['bundle_option'][$option->getId()][] = $selection->getSelectionId();
87+
$buyRequest['bundle_option_qty'][$option->getId()][$selection->getSelectionId()] = $qty;
88+
break;
89+
}
90+
}
91+
}
92+
}
93+
}
94+
return parent::apply(
95+
[
96+
'cart_id' => $data['cart_id'],
97+
'product_id' => $data['product_id'],
98+
'buy_request' => $buyRequest
99+
]
100+
);
101+
}
102+
}

app/code/Magento/Catalog/Model/Indexer/Product/Price/AbstractAction.php

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ abstract public function execute($ids);
171171
* @param array $processIds
172172
* @return AbstractAction
173173
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
174-
* @deprecated 102.0.6 Used only for backward compatibility for indexer, which not support indexation by dimensions
174+
* @deprecated 102.0.6
175+
* @see Used only for backward compatibility for indexer, which not support indexation by dimensions
175176
*/
176177
protected function _syncData(array $processIds = [])
177178
{
@@ -388,31 +389,33 @@ protected function _reindexRows($changedIds = [])
388389
$changedIds = array_unique(array_merge($changedIds, ...array_values($parentProductsTypes)));
389390
$productsTypes = array_merge_recursive($productsTypes, $parentProductsTypes);
390391

391-
if ($changedIds) {
392+
$typeIndexers = array_filter(
393+
$this->getTypeIndexers(),
394+
function ($type) use ($productsTypes) {
395+
return isset($productsTypes[$type]) && !empty($productsTypes[$type]);
396+
},
397+
ARRAY_FILTER_USE_KEY
398+
);
399+
if (empty($typeIndexers)) {
392400
$this->deleteIndexData($changedIds);
401+
return $changedIds;
393402
}
394403

395-
$typeIndexers = $this->getTypeIndexers();
396404
foreach ($typeIndexers as $productType => $indexer) {
397-
$entityIds = $productsTypes[$productType] ?? [];
398-
if (empty($entityIds)) {
399-
continue;
400-
}
401-
405+
$entityIds = $productsTypes[$productType];
402406
if ($indexer instanceof DimensionalIndexerInterface) {
403407
foreach ($this->dimensionCollectionFactory->create() as $dimensions) {
404408
$this->tableMaintainer->createMainTmpTable($dimensions);
405409
$temporaryTable = $this->tableMaintainer->getMainTmpTable($dimensions);
406410
$this->_emptyTable($temporaryTable);
407411
$indexer->executeByDimensions($dimensions, \SplFixedArray::fromArray($entityIds, false));
408-
// copy to index
409-
$this->_insertFromTable(
410-
$temporaryTable,
411-
$this->tableMaintainer->getMainTableByDimensions($dimensions)
412-
);
412+
$mainTable = $this->tableMaintainer->getMainTableByDimensions($dimensions);
413+
$this->_insertFromTable($temporaryTable, $mainTable);
414+
$this->deleteOutdatedData($entityIds, $temporaryTable, $mainTable);
413415
}
414416
} else {
415417
// handle 3d-party indexers for backward compatibility
418+
$this->deleteIndexData($changedIds);
416419
$this->_emptyTable($this->_defaultIndexerResource->getIdxTable());
417420
$this->_copyRelationIndexData($entityIds);
418421
$indexer->reindexEntity($entityIds);
@@ -423,6 +426,30 @@ protected function _reindexRows($changedIds = [])
423426
return $changedIds;
424427
}
425428

429+
/**
430+
* Delete records that do not exist anymore
431+
*
432+
* @param array $entityIds
433+
* @param string $temporaryTable
434+
* @param string $mainTable
435+
* @return void
436+
*/
437+
private function deleteOutdatedData(array $entityIds, string $temporaryTable, string $mainTable): void
438+
{
439+
$joinCondition = [
440+
'tmp_table.entity_id = main_table.entity_id',
441+
'tmp_table.customer_group_id = main_table.customer_group_id',
442+
'tmp_table.website_id = main_table.website_id',
443+
];
444+
$select = $this->getConnection()->select()
445+
->from(['main_table' => $mainTable], null)
446+
->joinLeft(['tmp_table' => $temporaryTable], implode(' AND ', $joinCondition), null)
447+
->where('tmp_table.entity_id IS NULL')
448+
->where('main_table.entity_id IN (?)', $entityIds, \Zend_Db::INT_TYPE);
449+
$query = $select->deleteFromSelect('main_table');
450+
$this->getConnection()->query($query);
451+
}
452+
426453
/**
427454
* Delete Index data index for list of entities
428455
*
@@ -449,7 +476,8 @@ private function deleteIndexData(array $entityIds)
449476
* @param null|array $parentIds
450477
* @param array $excludeIds
451478
* @return \Magento\Catalog\Model\Indexer\Product\Price\AbstractAction
452-
* @deprecated 102.0.6 Used only for backward compatibility for do not broke custom indexer implementation
479+
* @deprecated 102.0.6
480+
* @see Used only for backward compatibility for do not broke custom indexer implementation
453481
* which do not work by dimensions.
454482
* For indexers, which support dimensions all composite products read data directly from main price indexer table
455483
* or replica table for partial or full reindex correspondingly.

app/code/Magento/Catalog/Model/Product/Price/TierPriceFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ public function create(array $rawPrice, $sku)
9595
$price->setCustomerGroup(
9696
$rawPrice['all_groups'] == $this->allGroupsId
9797
? $this->allGroupsValue
98-
: $this->customerGroupRepository->getById($rawPrice['customer_group_id'])->getCode()
98+
: ($rawPrice['customer_group_code']
99+
?? $this->customerGroupRepository->getById($rawPrice['customer_group_id'])->getCode())
99100
);
100101
$price->setQuantity($rawPrice['qty']);
101102

app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Magento\Catalog\Model\Indexer\Product\Price\Processor as PriceIndexerProcessor;
1212
use Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator;
1313
use Magento\Catalog\Model\ProductIdLocatorInterface;
14+
use Magento\Customer\Model\ResourceModel\Group\GetCustomerGroupCodesByIds;
1415

1516
class TierPriceStorage implements TierPriceStorageInterface
1617
{
@@ -22,8 +23,6 @@ class TierPriceStorage implements TierPriceStorageInterface
2223
private $tierPricePersistence;
2324

2425
/**
25-
* Tier price validator.
26-
*
2726
* @var TierPriceValidator
2827
*/
2928
private $tierPriceValidator;
@@ -36,38 +35,42 @@ class TierPriceStorage implements TierPriceStorageInterface
3635
private $tierPriceFactory;
3736

3837
/**
39-
* Price index processor.
40-
*
4138
* @var PriceIndexerProcessor
4239
*/
4340
private $priceIndexProcessor;
4441

4542
/**
46-
* Product ID locator.
47-
*
4843
* @var ProductIdLocatorInterface
4944
*/
5045
private $productIdLocator;
5146

47+
/**
48+
* @var GetCustomerGroupCodesByIds
49+
*/
50+
private $getCustomerGroupCodesByIds;
51+
5252
/**
5353
* @param TierPricePersistence $tierPricePersistence
5454
* @param TierPriceValidator $tierPriceValidator
5555
* @param TierPriceFactory $tierPriceFactory
5656
* @param PriceIndexerProcessor $priceIndexProcessor
5757
* @param ProductIdLocatorInterface $productIdLocator
58+
* @param GetCustomerGroupCodesByIds $getCustomerGroupCodesByIds
5859
*/
5960
public function __construct(
6061
TierPricePersistence $tierPricePersistence,
6162
TierPriceValidator $tierPriceValidator,
6263
TierPriceFactory $tierPriceFactory,
6364
PriceIndexerProcessor $priceIndexProcessor,
64-
ProductIdLocatorInterface $productIdLocator
65+
ProductIdLocatorInterface $productIdLocator,
66+
GetCustomerGroupCodesByIds $getCustomerGroupCodesByIds
6567
) {
6668
$this->tierPricePersistence = $tierPricePersistence;
6769
$this->tierPriceValidator = $tierPriceValidator;
6870
$this->tierPriceFactory = $tierPriceFactory;
6971
$this->priceIndexProcessor = $priceIndexProcessor;
7072
$this->productIdLocator = $productIdLocator;
73+
$this->getCustomerGroupCodesByIds = $getCustomerGroupCodesByIds;
7174
}
7275

7376
/**
@@ -148,8 +151,22 @@ private function getExistingPrices(array $skus, bool $groupBySku = false): array
148151
if ($rawPrices) {
149152
$linkField = $this->tierPricePersistence->getEntityLinkField();
150153
$skuByIdLookup = $this->buildSkuByIdLookup($skus);
154+
$customerGroupCodesByIds = $this->getCustomerGroupCodesByIds->execute(
155+
array_column(
156+
array_filter(
157+
$rawPrices,
158+
static function (array $row) {
159+
return (int) $row['all_groups'] !== 1;
160+
}
161+
),
162+
'customer_group_id'
163+
),
164+
);
151165
foreach ($rawPrices as $rawPrice) {
152166
$sku = $skuByIdLookup[$rawPrice[$linkField]];
167+
if (isset($customerGroupCodesByIds[$rawPrice['customer_group_id']])) {
168+
$rawPrice['customer_group_code'] = $customerGroupCodesByIds[$rawPrice['customer_group_id']];
169+
}
153170
$price = $this->tierPriceFactory->create($rawPrice, $sku);
154171
if ($groupBySku) {
155172
$prices[$sku][] = $price;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,6 @@
4040
<element name="productOptionList" type="text" selector="#narrow-by-list"/>
4141
<element name="productNameByPosition" type="text" selector=".products-grid li:nth-of-type({{position}}) .product-item-name a" parameterized="true"/>
4242
<element name="sidebarAdditional" type="block" selector="#maincontent .sidebar.sidebar-additional"/>
43+
<element name="searchStore" type="input" selector="//div/input[@id='search']" />
4344
</section>
4445
</sections>

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@
9797
</assertStringContainsString>
9898
<!--Remove the category from the product and assert that it removed-->
9999
<comment userInput="Remove the category from the product and assert that it removed" stepKey="assertCategoryRemoved"/>
100+
<scrollTo selector='//label/span[contains(text(),"Quantity")]' stepKey="scrollToCategorySection"/>
101+
100102
<actionGroup ref="RemoveCategoryFromProductActionGroup" stepKey="removeCategoryFromProduct">
101103
<argument name="categoryName" value="$$createCategory.name$$"/>
102104
</actionGroup>

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@
1717
<severity value="BLOCKER"/>
1818
<testCaseId value="MC-25687"/>
1919
<group value="product"/>
20-
<skip>
21-
<issueId value="MQE-3515">Skipped</issueId>
22-
</skip>
2320
</annotations>
2421
<before>
2522
<actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>

0 commit comments

Comments
 (0)