Skip to content

Commit feaa32c

Browse files
committed
Merge remote-tracking branch 'origin/2.3-develop' into MAGETWO-90317
# Conflicts: # dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Store/Section/AdminStoresMainActionsSection.xml
2 parents f29dc42 + 1d35fa6 commit feaa32c

File tree

64 files changed

+2782
-774
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

+2782
-774
lines changed

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)