Skip to content

Commit d9a17d6

Browse files
committed
Merge branch '2.3-develop-main' into team3-delivery
2 parents d1645d0 + 65eb999 commit d9a17d6

File tree

64 files changed

+3231
-881
lines changed

Some content is hidden

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

64 files changed

+3231
-881
lines changed

README.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@
66
Welcome to Magento 2 installation! We're glad you chose to install Magento 2, a cutting-edge, feature-rich eCommerce solution that gets results.
77

88
## Magento system requirements
9-
[Magento system requirements](http://devdocs.magento.com/guides/v2.2/install-gde/system-requirements2.html)
9+
[Magento system requirements](http://devdocs.magento.com/guides/v2.3/install-gde/system-requirements2.html)
1010

1111
## Install Magento
1212
To install Magento, see either:
1313

14-
* [Magento DevBox](https://magento.com/tech-resources/download), the easiest way to get started with Magento.
15-
* [Installation guide](http://devdocs.magento.com/guides/v2.2/install-gde/bk-install-guide.html)
14+
* [Installation guide](http://devdocs.magento.com/guides/v2.3/install-gde/bk-install-guide.html)
1615

1716
<h2>Contributing to the Magento 2 code base</h2>
1817
Contributions can take the form of new components or features, changes to existing features, tests, documentation (such as developer guides, user guides, examples, or specifications), bug fixes, optimizations, or just good suggestions.
@@ -23,11 +22,24 @@ To learn about issues, click [here][2]. To open an issue, click [here][3].
2322

2423
To suggest documentation improvements, click [here][4].
2524

26-
[1]: <http://devdocs.magento.com/guides/v2.2/contributor-guide/contributing.html>
27-
[2]: <http://devdocs.magento.com/guides/v2.2/contributor-guide/contributing.html#report>
25+
[1]: <http://devdocs.magento.com/guides/v2.3/contributor-guide/contributing.html>
26+
[2]: <http://devdocs.magento.com/guides/v2.3/contributor-guide/contributing.html#report>
2827
[3]: <https://github.com/magento/magento2/issues>
2928
[4]: <http://devdocs.magento.com>
3029

30+
<h3>Community Maintainers</h3>
31+
The members of this team have been recognized for their outstanding commitment to maintaining and improving Magento. Magento has granted them permission to accept, merge, and reject pull requests, as well as review issues, and thanks these Community Maintainers for their valuable contributions.
32+
33+
<a href="https://magento.com/magento-contributors#maintainers">
34+
<img src="https://raw.githubusercontent.com/wiki/magento/magento2/images/maintainers.png"/>
35+
</a>
36+
37+
<h3>Top Contributors</h3>
38+
Magento team thanks for any contribution that can improve our code base, documentation or increase test coverage. We always recognize our most active members, your contributions are the foundation of the Magento open source platform.
39+
<a href="https://magento.com/magento-contributors">
40+
<img src="https://raw.githubusercontent.com/wiki/magento/magento2/images/contributors.png"/>
41+
</a>
42+
3143
<h3>Labels applied by the Magento team</h3>
3244

3345
| Label | Description |

app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<input type="hidden" name="product" value="<?= /* @escapeNotVerified */ $_product->getId() ?>" />
2323
<input type="hidden" name="selected_configurable_option" value="" />
2424
<input type="hidden" name="related_product" id="related-products-field" value="" />
25+
<input type="hidden" name="item" value="<?= /* @noEscape */ $block->getRequest()->getParam('id') ?>" />
2526
<?= $block->getBlockHtml('formkey') ?>
2627
<?= $block->getChildHtml('form_top') ?>
2728
<?php if (!$block->hasOptions()):?>

app/code/Magento/CatalogInventory/Model/ResourceModel/Stock.php

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,20 +113,20 @@ protected function _construct()
113113
}
114114

115115
/**
116-
* Lock Stock Item records
116+
* Lock Stock Item records.
117117
*
118118
* @param int[] $productIds
119119
* @param int $websiteId
120120
* @return array
121121
*/
122-
public function lockProductsStock($productIds, $websiteId)
122+
public function lockProductsStock(array $productIds, int $websiteId)
123123
{
124124
if (empty($productIds)) {
125125
return [];
126126
}
127127
$itemTable = $this->getTable('cataloginventory_stock_item');
128128
$select = $this->getConnection()->select()->from(['si' => $itemTable])
129-
->where('website_id=?', $websiteId)
129+
->where('website_id = ?', $websiteId)
130130
->where('product_id IN(?)', $productIds)
131131
->forUpdate(true);
132132

@@ -136,12 +136,19 @@ public function lockProductsStock($productIds, $websiteId)
136136
->columns(
137137
[
138138
'product_id' => 'entity_id',
139-
'type_id' => 'type_id'
139+
'type_id' => 'type_id',
140140
]
141141
);
142-
$this->getConnection()->query($select);
142+
$items = [];
143143

144-
return $this->getConnection()->fetchAll($selectProducts);
144+
foreach ($this->getConnection()->query($select)->fetchAll() as $si) {
145+
$items[$si['product_id']] = $si;
146+
}
147+
foreach ($this->getConnection()->fetchAll($selectProducts) as $p) {
148+
$items[$p['product_id']]['type_id'] = $p['type_id'];
149+
}
150+
151+
return $items;
145152
}
146153

147154
/**

app/code/Magento/CatalogInventory/Model/StockManagement.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,33 +50,42 @@ class StockManagement implements StockManagementInterface, RegisterProductSaleIn
5050
*/
5151
private $qtyCounter;
5252

53+
/**
54+
* @var StockRegistryStorage
55+
*/
56+
private $stockRegistryStorage;
57+
5358
/**
5459
* @param ResourceStock $stockResource
5560
* @param StockRegistryProviderInterface $stockRegistryProvider
5661
* @param StockState $stockState
5762
* @param StockConfigurationInterface $stockConfiguration
5863
* @param ProductRepositoryInterface $productRepository
5964
* @param QtyCounterInterface $qtyCounter
65+
* @param StockRegistryStorage|null $stockRegistryStorage
6066
*/
6167
public function __construct(
6268
ResourceStock $stockResource,
6369
StockRegistryProviderInterface $stockRegistryProvider,
6470
StockState $stockState,
6571
StockConfigurationInterface $stockConfiguration,
6672
ProductRepositoryInterface $productRepository,
67-
QtyCounterInterface $qtyCounter
73+
QtyCounterInterface $qtyCounter,
74+
StockRegistryStorage $stockRegistryStorage = null
6875
) {
6976
$this->stockRegistryProvider = $stockRegistryProvider;
7077
$this->stockState = $stockState;
7178
$this->stockConfiguration = $stockConfiguration;
7279
$this->productRepository = $productRepository;
7380
$this->qtyCounter = $qtyCounter;
7481
$this->resource = $stockResource;
82+
$this->stockRegistryStorage = $stockRegistryStorage ?: \Magento\Framework\App\ObjectManager::getInstance()
83+
->get(StockRegistryStorage::class);
7584
}
7685

7786
/**
7887
* Subtract product qtys from stock.
79-
* Return array of items that require full save
88+
* Return array of items that require full save.
8089
*
8190
* @param string[] $items
8291
* @param int $websiteId
@@ -94,17 +103,20 @@ public function registerProductsSale($items, $websiteId = null)
94103
$fullSaveItems = $registeredItems = [];
95104
foreach ($lockedItems as $lockedItemRecord) {
96105
$productId = $lockedItemRecord['product_id'];
106+
$this->stockRegistryStorage->removeStockItem($productId, $websiteId);
107+
97108
/** @var StockItemInterface $stockItem */
98109
$orderedQty = $items[$productId];
99110
$stockItem = $this->stockRegistryProvider->getStockItem($productId, $websiteId);
111+
$stockItem->setQty($lockedItemRecord['qty']); // update data from locked item
100112
$canSubtractQty = $stockItem->getItemId() && $this->canSubtractQty($stockItem);
101113
if (!$canSubtractQty || !$this->stockConfiguration->isQty($lockedItemRecord['type_id'])) {
102114
continue;
103115
}
104116
if (!$stockItem->hasAdminArea()
105117
&& !$this->stockState->checkQty($productId, $orderedQty, $stockItem->getWebsiteId())
106118
) {
107-
$this->getResource()->rollBack();
119+
$this->getResource()->commit();
108120
throw new \Magento\Framework\Exception\LocalizedException(
109121
__('Not all of your products are available in the requested quantity.')
110122
);
@@ -124,6 +136,7 @@ public function registerProductsSale($items, $websiteId = null)
124136
}
125137
$this->qtyCounter->correctItemsQty($registeredItems, $websiteId, '-');
126138
$this->getResource()->commit();
139+
127140
return $fullSaveItems;
128141
}
129142

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
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\CatalogInventory\Test\Unit\Model\ResourceModel;
9+
10+
use Magento\Catalog\Model\ResourceModel\Product\Collection;
11+
use Magento\CatalogInventory\Model\Configuration as StockConfiguration;
12+
use Magento\CatalogInventory\Model\ResourceModel\Stock;
13+
use Magento\Framework\App\Config;
14+
use Magento\Framework\DB\Adapter\Pdo\Mysql;
15+
use Magento\Framework\Model\ResourceModel\Db\Context;
16+
use Magento\Framework\Stdlib\DateTime\DateTime;
17+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
18+
use Magento\Store\Model\StoreManagerInterface;
19+
20+
/**
21+
* Test for \Magento\CatalogInventory\Model\ResourceModel\Stock
22+
*/
23+
class StockTest extends \PHPUnit\Framework\TestCase
24+
{
25+
const PRODUCT_TABLE = 'testProductTable';
26+
const ITEM_TABLE = 'testItemTableName';
27+
28+
/**
29+
* @var Stock|\PHPUnit_Framework_MockObject_MockObject
30+
*/
31+
private $stock;
32+
33+
/**
34+
* @var Mysql|\PHPUnit_Framework_MockObject_MockObject
35+
*/
36+
private $connectionMock;
37+
38+
/**
39+
* @var Config|\PHPUnit_Framework_MockObject_MockObject
40+
*/
41+
private $scopeConfigMock;
42+
43+
/**
44+
* @var DateTime|\PHPUnit_Framework_MockObject_MockObject
45+
*/
46+
private $dateTimeMock;
47+
48+
/**
49+
* @var StockConfiguration|\PHPUnit_Framework_MockObject_MockObject
50+
*/
51+
private $stockConfigurationMock;
52+
53+
/**
54+
* @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
55+
*/
56+
private $storeManagerMock;
57+
58+
/**
59+
* @var Context|\PHPUnit_Framework_MockObject_MockObject
60+
*/
61+
private $contextMock;
62+
63+
/**
64+
* @var \Magento\Framework\DB\Select|\PHPUnit_Framework_MockObject_MockObject
65+
*/
66+
private $selectMock;
67+
68+
/**
69+
* @var \Zend_Db_Statement_Interface|\PHPUnit_Framework_MockObject_MockObject
70+
*/
71+
private $statementMock;
72+
73+
protected function setUp()
74+
{
75+
$objectManager = new ObjectManager($this);
76+
$this->selectMock = $this->getMockBuilder(\Magento\Framework\DB\Select::class)
77+
->disableOriginalConstructor()
78+
->getMock();
79+
$this->contextMock = $objectManager->getObject(Context::class);
80+
$this->scopeConfigMock = $this->getMockBuilder(Config::class)
81+
->disableOriginalConstructor()
82+
->getMock();
83+
$this->dateTimeMock = $this->getMockBuilder(DateTime::class)
84+
->disableOriginalConstructor()
85+
->getMock();
86+
$this->stockConfigurationMock = $this->getMockBuilder(StockConfiguration::class)
87+
->setMethods(['getIsQtyTypeIds', 'getDefaultScopeId'])
88+
->disableOriginalConstructor()
89+
->getMock();
90+
$this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class)
91+
->disableOriginalConstructor()
92+
->getMock();
93+
$this->connectionMock = $this->getMockBuilder(Mysql::class)
94+
->disableOriginalConstructor()
95+
->getMock();
96+
$this->statementMock = $this->getMockForAbstractClass(\Zend_Db_Statement_Interface::class);
97+
$this->stock = $this->getMockBuilder(Stock::class)
98+
->setMethods(['getTable', 'getConnection'])
99+
->setConstructorArgs(
100+
[
101+
'context' => $this->contextMock,
102+
'scopeConfig' => $this->scopeConfigMock,
103+
'dateTime' => $this->dateTimeMock,
104+
'stockConfiguration' => $this->stockConfigurationMock,
105+
'storeManager' => $this->storeManagerMock,
106+
]
107+
)->getMock();
108+
}
109+
110+
/**
111+
* Test Save Product Status per website with product ids.
112+
*
113+
* @dataProvider productsDataProvider
114+
* @param int $websiteId
115+
* @param array $productIds
116+
* @param array $products
117+
* @param array $result
118+
*
119+
* @return void
120+
*/
121+
public function testLockProductsStock(int $websiteId, array $productIds, array $products, array $result)
122+
{
123+
$this->selectMock->expects($this->exactly(2))
124+
->method('from')
125+
->withConsecutive(
126+
[$this->identicalTo(['si' => self::ITEM_TABLE])],
127+
[$this->identicalTo(['p' => self::PRODUCT_TABLE]), $this->identicalTo([])]
128+
)
129+
->willReturnSelf();
130+
$this->selectMock->expects($this->exactly(3))
131+
->method('where')
132+
->withConsecutive(
133+
[$this->identicalTo('website_id = ?'), $this->identicalTo($websiteId)],
134+
[$this->identicalTo('product_id IN(?)'), $this->identicalTo($productIds)],
135+
[$this->identicalTo('entity_id IN (?)'), $this->identicalTo($productIds)]
136+
)
137+
->willReturnSelf();
138+
$this->selectMock->expects($this->once())
139+
->method('forUpdate')
140+
->with($this->identicalTo(true))
141+
->willReturnSelf();
142+
$this->selectMock->expects($this->once())
143+
->method('columns')
144+
->with($this->identicalTo(['product_id' => 'entity_id', 'type_id' => 'type_id']))
145+
->willReturnSelf();
146+
$this->connectionMock->expects($this->exactly(2))
147+
->method('select')
148+
->willReturn($this->selectMock);
149+
$this->connectionMock->expects($this->once())
150+
->method('query')
151+
->with($this->identicalTo($this->selectMock))
152+
->willReturn($this->statementMock);
153+
$this->statementMock->expects($this->once())
154+
->method('fetchAll')
155+
->willReturn($products);
156+
$this->connectionMock->expects($this->once())
157+
->method('fetchAll')
158+
->with($this->identicalTo($this->selectMock))
159+
->willReturn($result);
160+
$this->stock->expects($this->exactly(2))
161+
->method('getTable')
162+
->withConsecutive(
163+
[$this->identicalTo('cataloginventory_stock_item')],
164+
[$this->identicalTo('catalog_product_entity')]
165+
)->will($this->onConsecutiveCalls(
166+
self::ITEM_TABLE,
167+
self::PRODUCT_TABLE
168+
));
169+
$this->stock->expects($this->exactly(4))
170+
->method('getConnection')
171+
->willReturn($this->connectionMock);
172+
173+
$lockResult = $this->stock->lockProductsStock($productIds, $websiteId);
174+
175+
$this->assertEquals($result, $lockResult);
176+
}
177+
178+
/**
179+
* @return array
180+
*/
181+
public function productsDataProvider(): array
182+
{
183+
return [
184+
[
185+
0,
186+
[1, 2, 3],
187+
[
188+
1 => ['product_id' => 1],
189+
2 => ['product_id' => 2],
190+
3 => ['product_id' => 3],
191+
],
192+
[
193+
1 => [
194+
'product_id' => 1,
195+
'type_id' => 'simple',
196+
],
197+
2 => [
198+
'product_id' => 2,
199+
'type_id' => 'simple',
200+
],
201+
3 => [
202+
'product_id' => 3,
203+
'type_id' => 'simple',
204+
],
205+
],
206+
],
207+
];
208+
}
209+
}

0 commit comments

Comments
 (0)